From d7074e60997f430e3610f7050e5215b4c0b63163 Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 14 2020 11:50:31 +0000 Subject: Apply patch libgcrypt-1.7.3-fips-cavs.patch patch_name: libgcrypt-1.7.3-fips-cavs.patch present_in_specfile: true --- diff --git a/tests/cavs_driver.pl b/tests/cavs_driver.pl index bc93feb..f28748d 100755 --- a/tests/cavs_driver.pl +++ b/tests/cavs_driver.pl @@ -1,9 +1,11 @@ #!/usr/bin/env perl # -# $Id: cavs_driver.pl 1497 2009-01-22 14:01:29Z smueller $ +# $Id: cavs_driver.pl 2124 2010-12-20 07:56:30Z smueller $ # # CAVS test driver (based on the OpenSSL driver) # Written by: Stephan Müller +# Werner Koch (libgcrypt interface) +# Tomas Mraz (addition of DSA2) # Copyright (c) atsec information security corporation # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -85,13 +87,16 @@ # T[CBC|CFB??|ECB|OFB]varkey # T[CBC|CFB??|ECB|OFB]invperm # T[CBC|CFB??|ECB|OFB]vartext +# WARNING: TDES in CFB and OFB mode problems see below # # ANSI X9.31 RNG # ANSI931_AES128MCT # ANSI931_AES128VST # -# DSA +# DSA2 # PQGGen +# PQGVer +# KeyPair # SigGen # SigVer # @@ -101,6 +106,36 @@ # RC4PltBD # RC4REGT # +# +# TDES MCT for CFB and OFB: +# ------------------------- +# The inner loop cannot be handled by this script. If you want to have tests +# for these cipher types, implement your own inner loop and add it to +# crypto_mct. +# +# the value $next_source in crypto_mct is NOT set by the standard implementation +# of this script. It would need to be set as follows for these two (code take +# from fipsdrv.c from libgcrypt - the value input at the end will contain the +# the value for $next_source: +# +# ... inner loop ... +# ... +# get_current_iv (hd, last_iv, blocklen); +# ... encrypt / decrypt (input is the data to be en/decrypted and output is the +# result of operation) ... +# if (encrypt_mode && (cipher_mode == GCRY_CIPHER_MODE_CFB)) +# memcpy (input, last_iv, blocklen); +# else if (cipher_mode == GCRY_CIPHER_MODE_OFB) +# memcpy (input, last_iv, blocklen); +# else if (!encrypt_mode && cipher_mode == GCRY_CIPHER_MODE_CFB) +# { +# /* Reconstruct the output vector. */ +# int i; +# for (i=0; i < blocklen; i++) +# input[i] ^= output[i]; +# } +# ... inner loop ends ... +# ==> now, the value of input is to be put into $next_source use strict; use warnings; @@ -226,6 +261,8 @@ my $hmac; # Generate the P, Q, G, Seed, counter, h (value used to generate g) values # for DSA # $1: modulus size +# $2: q size +# $3: seed (might be empty string) # return: string with the calculated values in hex format, where each value # is separated from the previous with a \n in the following order: # P\n @@ -236,6 +273,19 @@ my $hmac; # h my $dsa_pqggen; +# Generate the G value from P and Q +# for DSA +# $1: modulus size +# $2: q size +# $3: P in hex form +# $4: Q in hex form +# return: string with the calculated values in hex format, where each value +# is separated from the previous with a \n in the following order: +# P\n +# Q\n +# G\n +my $dsa_ggen; + # # Generate an DSA public key from the provided parameters: # $1: Name of file to create @@ -255,16 +305,30 @@ my $dsa_verify; # generate a new DSA key with the following properties: # PEM format -# $1 keyfile name -# return: file created, hash with keys of P, Q, G in hex format +# $1: modulus size +# $2: q size +# $3 keyfile name +# return: file created with key, string with values of P, Q, G in hex format my $gen_dsakey; +# generate a new DSA private key XY parameters in domain: +# PEM format +# $1: P in hex form +# $2: Q in hex form +# $3: G in hex form +# return: string with values of X, Y in hex format +my $gen_dsakey_domain; + # Sign a message with DSA # $1: data to be signed in hex form # $2: Key file in PEM format with the private key # return: hash of digest information in hex format with Y, R, S as keys my $dsa_sign; +my $rsa_keygen; + +my $rsa_keygen_kat; + ################################################################ ##### OpenSSL interface functions ################################################################ @@ -404,6 +468,35 @@ sub libgcrypt_rsa_derive($$$$$$$$) { } +sub libgcrypt_rsa_keygen($) { + my $n = shift; + my $sexp; + + $n = sprintf ("%u", $n); + $sexp = "(genkey(rsa(nbits " . sprintf ("%u:%s", length($n), $n) . ")))\n"; + + return pipe_through_program($sexp, "fipsdrv rsa-keygen"); +} + + +sub libgcrypt_rsa_keygen_kat($$$$) { + my $n = shift; + my $e = shift; + my $p = shift; + my $q = shift; + my $sexp; + + $n = sprintf ("%u", $n); + $sexp = "(genkey(rsa(nbits " . sprintf ("%u:%s", length($n), $n) . ")" + . "(test-parms" + . "(e #$e#)" + . "(p #$p#)" + . "(q #$q#))))\n"; + + return pipe_through_program($sexp, "fipsdrv rsa-keygen-kat"); +} + + sub libgcrypt_rsa_sign($$$) { my $data = shift; my $hashalgo = shift; @@ -500,17 +593,32 @@ sub libgcrypt_hmac($$$$) { return pipe_through_program($msg, $program); } -sub libgcrypt_dsa_pqggen($) { +sub libgcrypt_dsa_pqggen($$$) { my $mod = shift; + my $qsize = shift; + my $seed = shift; + + my $program = "fipsdrv --keysize $mod --qsize $qsize dsa-pqg-gen"; + return pipe_through_program($seed, $program); +} - my $program = "fipsdrv --keysize $mod dsa-pqg-gen"; +sub libgcrypt_dsa_ggen($$$$) { + my $mod = shift; + my $qsize = shift; + my $p = shift; + my $q = shift; + my $domain = "(domain (p #$p#)(q #$q#))"; + + my $program = "fipsdrv --keysize $mod --qsize $qsize --key \'$domain\' dsa-g-gen"; return pipe_through_program("", $program); } -sub libgcrypt_gen_dsakey($) { +sub libgcrypt_gen_dsakey($$$) { + my $mod = shift; + my $qsize = shift; my $file = shift; - my $program = "fipsdrv --keysize 1024 --key $file dsa-gen"; + my $program = "fipsdrv --keysize $mod --qsize $qsize --key $file dsa-gen"; my $tmp; my %ret; @@ -519,10 +627,21 @@ sub libgcrypt_gen_dsakey($) { $tmp = pipe_through_program("", $program); die "dsa key gen failed: file $file not created" if (! -f $file); - @ret{'P', 'Q', 'G', 'Seed', 'c', 'H'} = split(/\n/, $tmp); + @ret{'P', 'Q', 'G'} = split(/\n/, $tmp); return %ret; } +sub libgcrypt_gen_dsakey_domain($$$) { + my $p = shift; + my $q = shift; + my $g = shift; + my $domain = "(domain (p #$p#)(q #$q#)(g #$g#))"; + + my $program = "fipsdrv --key '$domain' dsa-gen-key"; + + return pipe_through_program("", $program); +} + sub libgcrypt_dsa_genpubkey($$$$$) { my $filename = shift; my $p = shift; @@ -1139,7 +1258,7 @@ sub hmac_kat($$$$) { $out .= "Tlen = $tlen\n"; $out .= "Key = $key\n"; $out .= "Msg = $msg\n"; - $out .= "Mac = " . &$hmac($key, $tlen, $msg, $hashtype{$tlen}) . "\n"; + $out .= "Mac = " . lc(&$hmac($key, $tlen, $msg, $hashtype{$tlen})) . "\n"; return $out; } @@ -1205,7 +1324,7 @@ sub crypto_mct($$$$$$$$) { } my ($CO, $CI); my $cipher_imp = &$state_cipher($cipher, $enc, $bufsize, $key1, $iv); - $cipher_imp = &$state_cipher_des($cipher, $enc, $bufsize, $key1, $iv) if($cipher =~ /des/); + $cipher_imp = &$state_cipher_des($cipher, $enc, $bufsize, $key1, $iv) if($cipher =~ /des/ && defined($state_cipher_des)); my $pid = open2($CO, $CI, $cipher_imp); my $calc_data = $iv; # CT[j] @@ -1213,8 +1332,8 @@ sub crypto_mct($$$$$$$$) { my $old_old_calc_data; # CT[j-2] my $next_source; - # TDES inner loop implements logic within driver - if ($cipher =~ /des/) { + # TDES inner loop implements logic within driver of libgcrypt + if ($cipher =~ /des/ && $opt{'I'} && $opt{'I'} eq 'libgcrypt' ) { # Need to provide a dummy IV in case of ECB mode. my $iv_arg = (defined($iv) && $iv ne "") ? bin2hex($iv) @@ -1238,6 +1357,10 @@ sub crypto_mct($$$$$$$$) { $line = <$CO>; } else { for (my $j = 0; $j < $iloop; ++$j) { + if ($cipher =~ /des-ede3-ofb/ || + (!$enc && $cipher =~ /des-ede3-cfb/)) { + die "Implementation lacks support for TDES OFB and TDES CFB in encryption mode - the problem is that we would need to extract the IV of the last round of encryption which would be the input for the next round - see comments in this script for implementation requirements"; + } $old_old_calc_data = $old_calc_data; $old_calc_data = $calc_data; @@ -1429,7 +1552,7 @@ sub rsa_sigver($$$$$) { # $7 xq2 # $8 Xq # return: string formatted as expected by CAVS -sub rsa_keygen($$$$$$$$) { +sub rsa_keygen_x931($$$$$$$$) { my $modulus = shift; my $e = shift; my $xp1 = shift; @@ -1503,21 +1626,23 @@ sub rngx931($$$$) { return $out; } -# DSA PQGGen test +# DSA PQGen test # $1 modulus size -# $2 number of rounds to perform the test +# $2 q size +# $3 number of rounds to perform the test # return: string formatted as expected by CAVS -sub dsa_pqggen_driver($$) { +sub dsa_pqgen_driver($$$) { my $mod = shift; + my $qsize = shift; my $rounds = shift; my $out = ""; for(my $i=0; $i<$rounds; $i++) { - my $ret = &$dsa_pqggen($mod); + my $ret = &$dsa_pqggen($mod, $qsize, ""); my ($P, $Q, $G, $Seed, $c, $H) = split(/\n/, $ret); - die "Return value does not contain all expected values of P, Q, G, Seed, c, H for dsa_pqggen" - if (!defined($P) || !defined($Q) || !defined($G) || - !defined($Seed) || !defined($c) || !defined($H)); + die "Return value does not contain all expected values of P, Q, Seed, c for dsa_pqggen" + if (!defined($P) || !defined($Q) || + !defined($Seed) || !defined($c)); # now change the counter to decimal as CAVS wants decimal # counter value although all other is HEX @@ -1525,15 +1650,166 @@ sub dsa_pqggen_driver($$) { $out .= "P = $P\n"; $out .= "Q = $Q\n"; - $out .= "G = $G\n"; - $out .= "Seed = $Seed\n"; - $out .= "c = $c\n"; - $out .= "H = $H\n\n"; + $out .= "domain_parameter_seed = $Seed\n"; + $out .= "counter = $c\n\n"; } return $out; } +# DSA GGen test +# $1 modulus size +# $2 q size +# $3 p in hex form +# $4 q in hex form +# return: string formatted as expected by CAVS +sub dsa_ggen_driver($$$$) { + my $mod = shift; + my $qsize = shift; + my $p = shift; + my $q = shift; + + my $out = ""; + my $ret = &$dsa_ggen($mod, $qsize, $p, $q); + my ($P, $Q, $G) = split(/\n/, $ret); + die "Return value does not contain all expected values of P, Q, G for dsa_ggen" + if (!defined($P) || !defined($Q) || !defined($G)); + + $out .= "G = $G\n\n"; + + return $out; +} + +sub hexcomp($$) { + my $a = lc shift; + my $b = lc shift; + + if (length $a < length $b) { + my $c = $a; + $a = $b; + $b = $a; + } + + while (length $b < length $a) { + $b = "00$b"; + } + + return $a eq $b; +} + +# DSA PQVer test +# $1 modulus size +# $2 q size +# $3 p in hex form +# $4 q in hex form +# $5 seed in hex form +# $6 c decimal counter +# return: string formatted as expected by CAVS +sub dsa_pqver_driver($$$$$$) { + my $mod = shift; + my $qsize = shift; + my $p = shift; + my $q = shift; + my $seed = shift; + my $c = shift; + + my $out = ""; + my $ret = &$dsa_pqggen($mod, $qsize, $seed); + my ($P, $Q, $G, $seed2, $c2, $h2) = split(/\n/, $ret); + die "Return value does not contain all expected values of P, Q, G, seed, c for dsa_pqggen" + if (!defined($P) || !defined($Q) || !defined($G) || + !defined($seed2) || !defined($c2)); + + $c2 = hex($c2); + + $out .= "Seed = $seed\n"; + $out .= "c = $c\n"; + + if (hexcomp($P, $p) && hexcomp($Q, $q) && hexcomp($seed, $seed2) && $c == $c2) { + $out .= "Result = P\n\n"; + } + else { + $out .= "Result = F\n\n"; + } + return $out; +} + +# DSA PQGVer test +# $1 modulus size +# $2 q size +# $3 p in hex form +# $4 q in hex form +# $5 g in hex form +# $6 seed in hex form +# $7 c decimal counter +# $8 h in hex form +# return: string formatted as expected by CAVS +sub dsa_pqgver_driver($$$$$$$$) { + my $mod = shift; + my $qsize = shift; + my $p = shift; + my $q = shift; + my $g = shift; + my $seed = shift; + my $c = shift; + my $h = shift; + + my $out = ""; + my $ret = &$dsa_pqggen($mod, $qsize, $seed); + my ($P, $Q, $G, $seed2, $c2, $h2) = split(/\n/, $ret); + die "Return value does not contain all expected values of P, Q, G, seed, c, H for dsa_pqggen" + if (!defined($P) || !defined($Q) || !defined($G) || + !defined($seed2) || !defined($c2) || !defined($h2)); + + + + $out .= "Seed = $seed\n"; + $out .= "c = $c\n"; + $out .= "H = $h\n"; + + $c2 = hex($c2); + + if (hexcomp($P, $p) && hexcomp($Q, $q) && hexcomp($G, $g) && hexcomp($seed, $seed2) && + $c == $c2 && hex($h) == hex($h2)) { + $out .= "Result = P\n\n"; + } + else { + $out .= "Result = F\n\n"; + } + + return $out; +} + +# DSA Keypair test +# $1 modulus size +# $2 q size +# $3 number of rounds to perform the test +# return: string formatted as expected by CAVS +sub dsa_keypair_driver($$$) { + my $mod = shift; + my $qsize = shift; + my $rounds = shift; + + my $out = ""; + my $tmpkeyfile = "dsa_siggen.tmp.$$"; + my %pqg = &$gen_dsakey($mod, $qsize, $tmpkeyfile); + $out .= "P = " . $pqg{'P'} . "\n"; + $out .= "Q = " . $pqg{'Q'} . "\n"; + $out .= "G = " . $pqg{'G'} . "\n\n"; + unlink($tmpkeyfile); + + for(my $i=0; $i<$rounds; $i++) { + my $ret = &$gen_dsakey_domain($pqg{'P'}, $pqg{'Q'}, $pqg{'G'}); + my ($X, $Y) = split(/\n/, $ret); + die "Return value does not contain all expected values of X, Y for gen_dsakey_domain" + if (!defined($X) || !defined($Y)); + + $out .= "X = $X\n"; + $out .= "Y = $Y\n\n"; + } + + return $out; +} # DSA SigGen test # $1: Message to be signed in hex form @@ -1598,6 +1874,53 @@ sub dsa_sigver($$$$$$$$) { return $out; } +# RSA Keygen RPP test +# $1 modulus size +# $2 number of rounds to perform the test +# return: string formatted as expected by CAVS +sub rsa_keygen_driver($$) { + my $mod = shift; + my $rounds = shift; + + my $out = ""; + + for(my $i=0; $i<$rounds; $i++) { + my $ret = &$rsa_keygen($mod); + my ($e, $p, $q, $n, $d) = split(/\n/, $ret); + die "Return value does not contain all expected values of e, p, q, n, d for rsa_keygen" + if (!defined($e) || !defined($p) || !defined($q) || !defined($n) || !defined($d)); + + $out .= "e = $e\n"; + $out .= "p = $p\n"; + $out .= "q = $q\n"; + $out .= "n = $n\n"; + $out .= "d = $d\n\n"; + } + + return $out; +} + +# RSA RPP Keygen KAT test +# $1 modulus size +# $2 p in hex form +# $3 q in hex form +# return: string formatted as expected by CAVS +sub rsa_keygen_kat_driver($$$) { + my $mod = shift; + my $p = shift; + my $q = shift; + + my $out = ""; + my $ret = &$rsa_keygen_kat($mod, $p, $q); + my ($Result) = split(/\n/, $ret); + die "Return value does not contain all expected values of Result for rsa_keygen_kat" + if (!defined($Result)); + + $out .= "Result = $Result\n\n"; + return $out; +} + + ############################################################## # Parser of input file and generator of result file # @@ -1658,12 +1981,18 @@ sub parse($$) { my $klen = ""; my $tlen = ""; my $modulus = ""; + my $qsize = ""; my $capital_n = 0; + my $num = 0; my $capital_p = ""; my $capital_q = ""; my $capital_g = ""; my $capital_y = ""; my $capital_r = ""; + my $capital_h = ""; + my $c = ""; + my $prandom = ""; + my $qrandom = ""; my $xp1 = ""; my $xp2 = ""; my $Xp = ""; @@ -1700,7 +2029,7 @@ sub parse($$) { ##### Extract cipher # XXX there may be more - to be added - if ($tmpline =~ /^#.*(CBC|ECB|OFB|CFB|SHA-|SigGen|SigVer|RC4VS|ANSI X9\.31|Hash sizes tested|PQGGen|KeyGen RSA)/) { + if ($tmpline =~ /^#.*(CBC|ECB|OFB|CFB|SHA-|SigGen|SigVer|RC4VS|ANSI X9\.31|Hash sizes tested|PQGGen|KeyGen RSA|KeyGen - Random Probably Prime|KeyPair|PQGVer)/) { if ($tmpline =~ /CBC/) { $mode="cbc"; } elsif ($tmpline =~ /ECB/) { $mode="ecb"; } elsif ($tmpline =~ /OFB/) { $mode="ofb"; } @@ -1749,7 +2078,23 @@ sub parse($$) { if ($tt == 0) { ##### Identify the test type - if ($tmpline =~ /KeyGen RSA \(X9\.31\)/) { + if ($tmpline =~ /KeyGen - Random Probably Prime Known Answer Test/) { + $tt = 19; + die "Interface function rsa_keygen_kat for RSA key generation KAT not defined for tested library" + if (!defined($rsa_keygen_kat)); + } elsif ($tmpline =~ /KeyGen - Random Probably Prime Test/) { + $tt = 18; + die "Interface function rsa_keygen for RSA key generation not defined for tested library" + if (!defined($rsa_keygen)); + } elsif ($tmpline =~ /PQGVer/) { + $tt = 16; + die "Interface function for DSA PQGVer testing not defined for tested library" + if (!defined($dsa_pqggen)); + } elsif ($tmpline =~ /KeyPair/) { + $tt = 14; + die "Interface function dsa_keygen for DSA key generation not defined for tested library" + if (!defined($gen_dsakey_domain)); + } elsif ($tmpline =~ /KeyGen RSA \(X9\.31\)/) { $tt = 13; die "Interface function rsa_derive for RSA key generation not defined for tested library" if (!defined($rsa_derive)); @@ -1760,11 +2105,11 @@ sub parse($$) { } elsif ($tmpline =~ /SigGen/ && $opt{'D'}) { $tt = 11; die "Interface function dsa_sign or gen_dsakey for DSA sign not defined for tested library" - if (!defined($dsa_sign) || !defined($gen_rsakey)); + if (!defined($dsa_sign) || !defined($gen_dsakey)); } elsif ($tmpline =~ /PQGGen/) { $tt = 10; die "Interface function for DSA PQGGen testing not defined for tested library" - if (!defined($dsa_pqggen)); + if (!defined($dsa_pqggen) || !defined($dsa_ggen)); } elsif ($tmpline =~ /Hash sizes tested/) { $tt = 9; die "Interface function hmac for HMAC testing not defined for tested library" @@ -1792,7 +2137,7 @@ sub parse($$) { } elsif ($tmpline =~ /Monte|MCT|Carlo/) { $tt = 2; die "Interface function state_cipher for Stateful Cipher operation defined for tested library" - if (!defined($state_cipher) || !defined($state_cipher_des)); + if (!defined($state_cipher) && !defined($state_cipher_des)); } elsif ($cipher =~ /^sha/) { $tt = 3; die "Interface function hash for Hashing not defined for tested library" @@ -1875,18 +2220,44 @@ sub parse($$) { die "Msg/Seed seen twice - input file crap" if ($pt ne ""); $pt=$2; } - elsif ($line =~ /^\[mod\s*=\s*(.*)\]$/) { # found in RSA requests + elsif ($line =~ /^\[A.2.1\s.*\]$/) { # found in DSA2 PQGGen request + $out .= $line . "\n"; # print it + if ($tt == 10) { + # now generate G from PQ + $tt = 15; + } + } + elsif ($line =~ /^\[A.2.2\s.*\]$/) { # found in DSA2 PQGVer request + $out .= $line . "\n"; # print it + if ($tt == 16) { + # now verify PQG + $tt = 17; + } + } + elsif ($line =~ /^\[mod\s*=\s*L=([0-9]*),\s*N=([0-9]*).*\]$/) { # found in DSA2 requests $modulus = $1; + $qsize = $2; $out .= $line . "\n\n"; # print it + # clear eventual PQG + $capital_p = ""; + $capital_q = ""; + $capital_g = ""; # generate the private key with given bit length now # as we have the required key length in bit if ($tt == 11) { $dsa_keyfile = "dsa_siggen.tmp.$$"; - my %pqg = &$gen_dsakey($dsa_keyfile); + my %pqg = &$gen_dsakey($modulus, $qsize, $dsa_keyfile); $out .= "P = " . $pqg{'P'} . "\n"; $out .= "Q = " . $pqg{'Q'} . "\n"; - $out .= "G = " . $pqg{'G'} . "\n"; - } elsif ( $tt == 5 ) { + $out .= "G = " . $pqg{'G'} . "\n\n"; + } + } + elsif ($line =~ /^\[mod\s*=\s*(.*)\]$/) { # found in RSA requests + $modulus = $1; + $out .= $line . "\n\n"; # print it + # generate the private key with given bit length now + # as we have the required key length in bit + if ( $tt == 5 ) { # XXX maybe a secure temp file name is better here # but since it is not run on a security sensitive # system, I hope that this is fine @@ -1907,6 +2278,9 @@ sub parse($$) { } elsif ($line =~ /^e\s*=\s*(.*)/) { # found in RSA requests $e=$1; + if ($tt == 19) { + $out .= $line . "\n"; # print it + } } elsif ($line =~ /^S\s*=\s*(.*)/) { # found in RSA requests die "S seen twice - input file crap" if ($signature ne ""); @@ -1932,11 +2306,16 @@ sub parse($$) { if ($tlen ne ""); $tlen=$1; } - elsif ($line =~ /^N\s*=\s*(.*)/) { #DSA PQGGen + elsif ($line =~ /^N\s*=\s*(.*)/) { #DSA KeyPair die "N seen twice - check input file" if ($capital_n); $capital_n = $1; } + elsif ($line =~ /^Num\s*=\s*(.*)/) { #DSA PQGGen + die "Num seen twice - check input file" + if ($num); + $num = $1; + } elsif ($line =~ /^P\s*=\s*(.*)/) { #DSA SigVer die "P seen twice - check input file" if ($capital_p); @@ -1965,6 +2344,16 @@ sub parse($$) { if ($capital_r); $capital_r = $1; } + elsif ($line =~ /^H\s*=\s*(.*)/) { #DSA PQGVer + die "H seen twice - check input file" + if ($capital_h); + $capital_h = $1; + } + elsif ($line =~ /^c\s*=\s*(.*)/) { #DSA PQGVer + die "c seen twice - check input file" + if ($c); + $c = $1; + } elsif ($line =~ /^xp1\s*=\s*(.*)/) { #RSA key gen die "xp1 seen twice - check input file" if ($xp1); @@ -1995,6 +2384,22 @@ sub parse($$) { if ($Xq); $Xq = $1; } + elsif ($line =~ /^prandom\s*=\s*(.*)/) { #RSA key gen KAT + die "prandom seen twice - check input file" + if ($prandom); + $prandom = $1; + $out .= $line . "\n"; # print it + } + elsif ($line =~ /^qrandom\s*=\s*(.*)/) { #RSA key gen KAT + die "qrandom seen twice - check input file" + if ($qrandom); + $qrandom = $1; + $out .= $line . "\n"; # print it + } + elsif ($tt == 19 && $line =~ /^ / && $qrandom eq "") { #RSA key gen KAT + $qrandom = "00"; + $out .= $line . "\n"; # print it + } else { $out .= $line . "\n"; } @@ -2074,11 +2479,10 @@ sub parse($$) { } } elsif ($tt == 10) { - if ($modulus ne "" && $capital_n > 0) { - $out .= dsa_pqggen_driver($modulus, $capital_n); - #$mod is not resetted - $capital_n = 0; - } + if ($modulus ne "" && $qsize ne "" && $num > 0) { + $out .= dsa_pqgen_driver($modulus, $qsize, $num); + $num = 0; + } } elsif ($tt == 11) { if ($pt ne "" && $dsa_keyfile ne "") { @@ -2124,7 +2528,7 @@ sub parse($$) { $xq1 ne "" && $xq2 ne "" && $Xq ne "") { - $out .= rsa_keygen($modulus, + $out .= rsa_keygen_x931($modulus, $e, $xp1, $xp2, @@ -2141,6 +2545,96 @@ sub parse($$) { $Xq = ""; } } + elsif ($tt == 14) { + if ($modulus ne "" && + $qsize ne "" && + $capital_n > 0) { + $out .= dsa_keypair_driver($modulus, + $qsize, + $capital_n); + $capital_n = 0; + } + } + elsif ($tt == 15) { + if ($modulus ne "" && + $qsize ne "" && + $capital_p ne "" && + $capital_q ne "") { + $out .= dsa_ggen_driver($modulus, + $qsize, + $capital_p, + $capital_q); + $capital_p = ""; + $capital_q = ""; + $num--; + } + } + elsif ($tt == 16) { + if ($modulus ne "" && + $qsize ne "" && + $capital_p ne "" && + $capital_q ne "" && + $pt ne "" && + $c ne "") { + $out .= dsa_pqver_driver($modulus, + $qsize, + $capital_p, + $capital_q, + $pt, + $c); + $capital_p = ""; + $capital_q = ""; + $pt = ""; + $c = ""; + } + } + elsif ($tt == 17) { + if ($modulus ne "" && + $qsize ne "" && + $capital_p ne "" && + $capital_q ne "" && + $capital_g ne "" && + $pt ne "" && + $c ne "" && + $capital_h ne "") { + $out .= dsa_pqgver_driver($modulus, + $qsize, + $capital_p, + $capital_q, + $capital_g, + $pt, + $c, + $capital_h); + $capital_p = ""; + $capital_q = ""; + $capital_g = ""; + $pt = ""; + $c = ""; + $capital_h = ""; + } + } + elsif ($tt == 18) { + if ($modulus ne "" && + $capital_n > 0) { + $out .= rsa_keygen_driver($modulus, + $capital_n); + $capital_n = 0; + } + } + elsif ($tt == 19) { + if ($modulus ne "" && + $e ne "" && + $prandom ne "" && + $qrandom ne "") { + $out .= rsa_keygen_kat_driver($modulus, + $e, + $prandom, + $qrandom); + $prandom = ""; + $qrandom = ""; + $e = ""; + } + } elsif ($tt > 0) { die "Test case $tt not defined"; } @@ -2199,10 +2693,14 @@ sub main() { $state_rng = \&libgcrypt_state_rng; $hmac = \&libgcrypt_hmac; $dsa_pqggen = \&libgcrypt_dsa_pqggen; + $dsa_ggen = \&libgcrypt_dsa_ggen; $gen_dsakey = \&libgcrypt_gen_dsakey; + $gen_dsakey_domain = \&libgcrypt_gen_dsakey_domain; $dsa_sign = \&libgcrypt_dsa_sign; $dsa_verify = \&libgcrypt_dsa_verify; $dsa_genpubkey = \&libgcrypt_dsa_genpubkey; + $rsa_keygen = \&libgcrypt_rsa_keygen; + $rsa_keygen_kat = \&libgcrypt_rsa_keygen_kat; } else { die "Invalid interface option given"; } diff --git a/tests/cavs_driver.pl.cavs b/tests/cavs_driver.pl.cavs new file mode 100755 index 0000000..bc93feb --- /dev/null +++ b/tests/cavs_driver.pl.cavs @@ -0,0 +1,2243 @@ +#!/usr/bin/env perl +# +# $Id: cavs_driver.pl 1497 2009-01-22 14:01:29Z smueller $ +# +# CAVS test driver (based on the OpenSSL driver) +# Written by: Stephan Müller +# Copyright (c) atsec information security corporation +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# NO WARRANTY +# +# BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +# FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +# OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +# PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +# OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +# TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +# PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +# REPAIR OR CORRECTION. +# +# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +# WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +# REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +# OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +# YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGES. +# +# +# test execution instruction: +# 1. get the request files from the lab +# 2. call each request file from 1. with this program: +# $0 .rep +# 3. send the resulting file .rsp to the lab +# +# +# Test should be easily adoptable to other implementations +# See the first functions for this task +# +# Following tests are covered (others may also be covered +# but have not been tested) +# +# AES +# [CBC|CFB128|ECB|OFB]GFSbox[128|192|256] +# [CBC|CFB128|ECB|OFB]MCT[128|192|256] +# [CBC|CFB128|ECB|OFB]VarKey[128|192|256] +# [CBC|CFB128|ECB|OFB]KeySbox[128|192|256] +# [CBC|CFB128|ECB|OFB]MMT[128|192|256] +# [CBC|CFB128|ECB|OFB]VarTxt[128|192|256] +# +# RSA +# SigGen[15|RSA] +# SigVer15 +# (SigVerRSA is not applicable for OpenSSL as X9.31 padding +# is not done through openssl dgst) +# KeyGen RSA X9.31 +# +# SHA +# SHA[1|224|256|384|512]ShortMsg +# SHA[1|224|256|384|512]LongMsg +# SHA[1|224|256|384|512]Monte +# +# HMAC (SHA - caveat: we only support hash output equal to the block size of +# of the hash - we do not support truncation of the hash; to support +# that, we first need to decipher the HMAC.req file - see hmac_kat() ) +# HMAC +# +# TDES +# T[CBC|CFB??|ECB|OFB]Monte[1|2|3] +# T[CBC|CFB??|ECB|OFB]permop +# T[CBC|CFB??|ECB|OFB]MMT[1|2|3] +# T[CBC|CFB??|ECB|OFB]subtab +# T[CBC|CFB??|ECB|OFB]varkey +# T[CBC|CFB??|ECB|OFB]invperm +# T[CBC|CFB??|ECB|OFB]vartext +# +# ANSI X9.31 RNG +# ANSI931_AES128MCT +# ANSI931_AES128VST +# +# DSA +# PQGGen +# SigGen +# SigVer +# +# RC4 (atsec developed tests) +# RC4KeyBD +# RC4MCT +# RC4PltBD +# RC4REGT +# + +use strict; +use warnings; +use IPC::Open2; +use Getopt::Std; +use MIME::Base64; + +# Contains the command line options +my %opt; + +################################################################# +##### Central interface functions to the external ciphers ####### +################################################################# +# Only these interface routines should be changed in case of +# porting to a new cipher library +# +# For porting to a new library, create implementation of these functions +# and then add pointers to the respective implementation of each +# function to the given variables. + +# common encryption/decryption routine +# $1 key in hex form (please note for 3DES: even when ede3 for three +# independent ciphers is given with the cipher specification, we hand in +# either one key for k1 = k2 = k3, two keys which are concatinated for +# k1 = k3, k2 independent, or three keys which are concatinated for +# k1, k2, k3 independent) +# $2 iv in hex form +# $3 cipher - the cipher string is defined as specified in the openssl +# enc(1ssl) specification for the option "-ciphername" +# (e.g. aes-128-cbc or des-ede3-cbc) +# $4 encrypt=1/decrypt=0 +# $5 de/encrypted data in hex form +# return en/decrypted data in hex form +my $encdec; + +# +# Derive an RSA key from the given X9.31 parameters. +# $1: modulus size +# $2: E in hex form +# $3: Xp1 in hex form +# $4: Xp2 in hex form +# $5: Xp in hex form +# $6: Xq1 in hex form +# $7: Xq2 in hex form +# $8: Xq in hex form +# return: string with the calculated values in hex format, where each value +# is separated from the previous with a \n in the following order: +# P\n +# Q\n +# N\n +# D\n +my $rsa_derive; + +# Sign a message with RSA +# $1: data to be signed in hex form +# $2: Hash algo +# $3: Key file in PEM format with the private key +# return: digest in hex format +my $rsa_sign; + +# Verify a message with RSA +# $1: data to be verified in hex form +# $2: hash algo +# $3: file holding the public RSA key in PEM format +# $4: file holding the signature in binary form +# return: 1 == verified / 0 == not verified +my $rsa_verify; + +# generate a new private RSA key with the following properties: +# exponent is 65537 +# PEM format +# $1 key size in bit +# $2 keyfile name +# return: nothing, but file created +my $gen_rsakey; + +# Creating a hash +# $1: Plaintext in hex form +# $2: hash type in the form documented in openssl's dgst(1ssl) - e.g. +# sha1, sha224, sha256, sha384, sha512 +# return: hash in hex form +my $hash; + +# supplying the call to the external cipher implementation +# that is being used to keep STDIN and STDOUT open +# to maintain the state of the block chaining +# $1: cipher +# $2: 1=encryption, 0=decryption +# $3: buffersize needed for openssl +# $4: encryption key in binary form +# $5: IV in binary form +# return: command line to execute the application +my $state_cipher; +# the only difference of the DES version is that it implements the inner loop +# of the TDES tests +my $state_cipher_des; + +# supplying the call to the external cipher implementation +# that is being used to keep STDIN and STDOUT open +# to maintain the state of the RNG with its seed +# +# input holds seed values +# $1: cipher key in hex format +# $2: DT value in hex format +# $3: V value in hex format +# +# return: command line to execute the application +# +# the application is expected to deliver random values on STDOUT - the script +# reads 128 bits repeatedly where the state of the RNG must be retained +# between the reads. The output of the RNG on STDOUT is assumed to be binary. +my $state_rng; + +# Generate an HMAC based on SHAx +# $1: Key to be used for the HMAC in hex format +# $2: length of the hash to be calculated in bits +# $3: Message for which the HMAC shall be calculated in hex format +# $4: hash type (1 - SHA1, 224 - SHA224, and so on) +# return: calculated HMAC in hex format +my $hmac; + +# +# Generate the P, Q, G, Seed, counter, h (value used to generate g) values +# for DSA +# $1: modulus size +# return: string with the calculated values in hex format, where each value +# is separated from the previous with a \n in the following order: +# P\n +# Q\n +# G\n +# Seed\n +# counter\n +# h +my $dsa_pqggen; + +# +# Generate an DSA public key from the provided parameters: +# $1: Name of file to create +# $2: P in hex form +# $3: Q in hex form +# $4: G in hex form +# $5: Y in hex form +my $dsa_genpubkey; + +# Verify a message with DSA +# $1: data to be verified in hex form +# $2: file holding the public DSA key in PEM format +# $3: R value of the signature +# $4: S value of the signature +# return: 1 == verified / 0 == not verified +my $dsa_verify; + +# generate a new DSA key with the following properties: +# PEM format +# $1 keyfile name +# return: file created, hash with keys of P, Q, G in hex format +my $gen_dsakey; + +# Sign a message with DSA +# $1: data to be signed in hex form +# $2: Key file in PEM format with the private key +# return: hash of digest information in hex format with Y, R, S as keys +my $dsa_sign; + +################################################################ +##### OpenSSL interface functions +################################################################ +sub openssl_encdec($$$$$) { + my $key=shift; + my $iv=shift; + my $cipher=shift; + my $enc = (shift) ? "-e" : "-d"; + my $data=shift; + + # We only invoke the driver with the IV parameter, if we have + # an IV, otherwise, we skip it + $iv = "-iv $iv" if ($iv); + + $data=hex2bin($data); + my $program="openssl enc -$cipher -nopad -nosalt -K $key $enc $iv"; + $program = "rc4 -k $key" if $opt{'R'}; #for ARCFOUR, no IV must be given + $data=pipe_through_program($data,$program); + return bin2hex($data); +} + +sub openssl_rsa_sign($$$) { + my $data = shift; + my $cipher = shift; + my $keyfile = shift; + + $data=hex2bin($data); + die "ARCFOUR not available for RSA" if $opt{'R'}; + $data=pipe_through_program($data, + "openssl dgst -$cipher -binary -sign $keyfile"); + return bin2hex($data); +} + +sub openssl_rsa_verify($$$$) { + my $data = shift; + my $cipher = shift; + my $keyfile = shift; + my $sigfile = shift; + + $data = hex2bin($data); + die "ARCFOUR not available for RSA" if $opt{'R'}; + $data = pipe_through_program($data, + "openssl dgst -$cipher -binary -verify $keyfile -signature $sigfile"); + + # Parse through the OpenSSL output information + return ($data =~ /OK/); +} + +sub openssl_gen_rsakey($$) { + my $keylen = shift; + my $file = shift; + + die "ARCFOUR not available for RSA" if $opt{'R'}; + # generating of a key with exponent 0x10001 + my @args = ("openssl", "genrsa", "-F4", "-out", "$file", "$keylen"); + system(@args) == 0 + or die "system @args failed: $?"; + die "system @args failed: file $file not created" if (! -f $file); +} + +sub openssl_hash($$) { + my $pt = shift; + my $cipher = shift; + + die "ARCFOUR not available for hashes" if $opt{'R'}; + my $hash = hex2bin($pt); + #bin2hex not needed as the '-hex' already converts it + return pipe_through_program($hash, "openssl dgst -$cipher -hex"); +} + +sub openssl_state_cipher($$$$$) { + my $cipher = shift; + my $encdec = shift; + my $bufsize = shift; + my $key = shift; + my $iv = shift; + + my $enc = $encdec ? "-e": "-d"; + + # We only invoke the driver with the IV parameter, if we have + # an IV, otherwise, we skip it + $iv = "-iv ".bin2hex($iv) if ($iv); + + my $out = "openssl enc -'$cipher' $enc -nopad -nosalt -bufsize $bufsize -K ".bin2hex($key)." $iv"; + #for ARCFOUR, no IV must be given + $out = "rc4 -k " . bin2hex($key) if $opt{'R'}; + return $out; +} + +###### End of OpenSSL interface implementation ############ + +########################################################### +###### libgcrypt implementation +########################################################### +sub libgcrypt_encdec($$$$$) { + my $key=shift; + my $iv=shift; + my $cipher=shift; + my $enc = (shift) ? "encrypt" : "decrypt"; + my $data=shift; + + # We only invoke the driver with the IV parameter, if we have + # an IV, otherwise, we skip it + $iv = "--iv $iv" if ($iv); + + my $program="fipsdrv --key $key $iv --algo $cipher $enc"; + + return pipe_through_program($data,$program); + +} + +sub libgcrypt_rsa_derive($$$$$$$$) { + my $n = shift; + my $e = shift; + my $xp1 = shift; + my $xp2 = shift; + my $xp = shift; + my $xq1 = shift; + my $xq2 = shift; + my $xq = shift; + my $sexp; + my @tmp; + + $n = sprintf ("%u", $n); + $e = sprintf ("%u", hex($e)); + $sexp = "(genkey(rsa(nbits " . sprintf ("%u:%s", length($n), $n) . ")" + . "(rsa-use-e " . sprintf ("%u:%s", length($e), $e) . ")" + . "(derive-parms" + . "(Xp1 #$xp1#)" + . "(Xp2 #$xp2#)" + . "(Xp #$xp#)" + . "(Xq1 #$xq1#)" + . "(Xq2 #$xq2#)" + . "(Xq #$xq#))))\n"; + + return pipe_through_program($sexp, "fipsdrv rsa-derive"); +} + + +sub libgcrypt_rsa_sign($$$) { + my $data = shift; + my $hashalgo = shift; + my $keyfile = shift; + + die "ARCFOUR not available for RSA" if $opt{'R'}; + + return pipe_through_program($data, + "fipsdrv --pkcs1 --algo $hashalgo --key $keyfile rsa-sign"); +} + +sub libgcrypt_rsa_verify($$$$) { + my $data = shift; + my $hashalgo = shift; + my $keyfile = shift; + my $sigfile = shift; + + die "ARCFOUR not available for RSA" if $opt{'R'}; + $data = pipe_through_program($data, + "fipsdrv --pkcs1 --algo $hashalgo --key $keyfile --signature $sigfile rsa-verify"); + + # Parse through the output information + return ($data =~ /GOOD signature/); +} + +sub libgcrypt_gen_rsakey($$) { + my $keylen = shift; + my $file = shift; + + die "ARCFOUR not available for RSA" if $opt{'R'}; + my @args = ("fipsdrv --keysize $keylen rsa-gen > $file"); + system(@args) == 0 + or die "system @args failed: $?"; + die "system @args failed: file $file not created" if (! -f $file); +} + +sub libgcrypt_hash($$) { + my $pt = shift; + my $hashalgo = shift; + + my $program = "fipsdrv --algo $hashalgo digest"; + die "ARCFOUR not available for hashes" if $opt{'R'}; + + return pipe_through_program($pt, $program); +} + +sub libgcrypt_state_cipher($$$$$) { + my $cipher = shift; + my $enc = (shift) ? "encrypt": "decrypt"; + my $bufsize = shift; + my $key = shift; + my $iv = shift; + + # We only invoke the driver with the IV parameter, if we have + # an IV, otherwise, we skip it + $iv = "--iv ".bin2hex($iv) if ($iv); + + my $program="fipsdrv --binary --key ".bin2hex($key)." $iv --algo '$cipher' --chunk '$bufsize' $enc"; + + return $program; +} + +sub libgcrypt_state_cipher_des($$$$$) { + my $cipher = shift; + my $enc = (shift) ? "encrypt": "decrypt"; + my $bufsize = shift; + my $key = shift; + my $iv = shift; + + # We only invoke the driver with the IV parameter, if we have + # an IV, otherwise, we skip it + $iv = "--iv ".bin2hex($iv) if ($iv); + + my $program="fipsdrv --algo '$cipher' --mct-server $enc"; + + return $program; +} + +sub libgcrypt_state_rng($$$) { + my $key = shift; + my $dt = shift; + my $v = shift; + + return "fipsdrv --binary --loop --key $key --iv $v --dt $dt random"; +} + +sub libgcrypt_hmac($$$$) { + my $key = shift; + my $maclen = shift; + my $msg = shift; + my $hashtype = shift; + + my $program = "fipsdrv --key $key --algo $hashtype hmac-sha"; + return pipe_through_program($msg, $program); +} + +sub libgcrypt_dsa_pqggen($) { + my $mod = shift; + + my $program = "fipsdrv --keysize $mod dsa-pqg-gen"; + return pipe_through_program("", $program); +} + +sub libgcrypt_gen_dsakey($) { + my $file = shift; + + my $program = "fipsdrv --keysize 1024 --key $file dsa-gen"; + my $tmp; + my %ret; + + die "ARCFOUR not available for DSA" if $opt{'R'}; + + $tmp = pipe_through_program("", $program); + die "dsa key gen failed: file $file not created" if (! -f $file); + + @ret{'P', 'Q', 'G', 'Seed', 'c', 'H'} = split(/\n/, $tmp); + return %ret; +} + +sub libgcrypt_dsa_genpubkey($$$$$) { + my $filename = shift; + my $p = shift; + my $q = shift; + my $g = shift; + my $y = shift; + + my $sexp; + + $sexp = "(public-key(dsa(p #$p#)(q #$q#)(g #$g#)(y #$y#)))"; + + open(FH, ">", $filename) or die; + print FH $sexp; + close FH; +} + +sub libgcrypt_dsa_sign($$) { + my $data = shift; + my $keyfile = shift; + my $tmp; + my %ret; + + die "ARCFOUR not available for DSA" if $opt{'R'}; + + $tmp = pipe_through_program($data, "fipsdrv --key $keyfile dsa-sign"); + @ret{'Y', 'R', 'S'} = split(/\n/, $tmp); + return %ret; +} + +sub libgcrypt_dsa_verify($$$$) { + my $data = shift; + my $keyfile = shift; + my $r = shift; + my $s = shift; + + my $ret; + + die "ARCFOUR not available for DSA" if $opt{'R'}; + + my $sigfile = "$keyfile.sig"; + open(FH, ">$sigfile") or die "Cannot create file $sigfile: $?"; + print FH "(sig-val(dsa(r #$r#)(s #$s#)))"; + close FH; + + $ret = pipe_through_program($data, + "fipsdrv --key $keyfile --signature $sigfile dsa-verify"); + unlink ($sigfile); + # Parse through the output information + return ($ret =~ /GOOD signature/); +} + +######### End of libgcrypt implementation ################ + +################################################################ +###### Vendor1 interface functions +################################################################ + +sub vendor1_encdec($$$$$) { + my $key=shift; + my $iv=shift; + my $cipher=shift; + my $enc = (shift) ? "encrypt" : "decrypt"; + my $data=shift; + + $data=hex2bin($data); + my $program = "./aes $enc $key"; + $data=pipe_through_program($data,$program); + return bin2hex($data); +} + +sub vendor1_state_cipher($$$$$) { + my $cipher = shift; + my $encdec = shift; + my $bufsize = shift; + my $key = shift; + my $iv = shift; + + $key = bin2hex($key); + my $enc = $encdec ? "encrypt": "decrypt"; + my $out = "./aes $enc $key $bufsize"; + return $out; +} + +##### No other interface functions below this point ###### +########################################################## + +########################################################## +# General helper routines + +# Executing a program by feeding STDIN and retrieving +# STDOUT +# $1: data string to be piped to the app on STDIN +# rest: program and args +# returns: STDOUT of program as string +sub pipe_through_program($@) { + my $in = shift; + my @args = @_; + + my ($CO, $CI); + my $pid = open2($CO, $CI, @args); + + my $out = ""; + my $len = length($in); + my $first = 1; + while (1) { + my $rin = ""; + my $win = ""; + # Output of prog is FD that we read + vec($rin,fileno($CO),1) = 1; + # Input of prog is FD that we write + # check for $first is needed because we can have NULL input + # that is to be written to the app + if ( $len > 0 || $first) { + (vec($win,fileno($CI),1) = 1); + $first=0; + } + # Let us wait for 100ms + my $nfound = select(my $rout=$rin, my $wout=$win, undef, 0.1); + if ( $wout ) { + my $written = syswrite($CI, $in, $len); + die "broken pipe" if !defined $written; + $len -= $written; + substr($in, 0, $written) = ""; + if ($len <= 0) { + close $CI or die "broken pipe: $!"; + } + } + if ( $rout ) { + my $tmp_out = ""; + my $bytes_read = sysread($CO, $tmp_out, 4096); + $out .= $tmp_out; + last if ($bytes_read == 0); + } + } + close $CO or die "broken pipe: $!"; + waitpid $pid, 0; + + return $out; +} + +# +# convert ASCII hex to binary input +# $1 ASCII hex +# return binary representation +sub hex2bin($) { + my $in = shift; + my $len = length($in); + $len = 0 if ($in eq "00"); + return pack("H$len", "$in"); +} + +# +# convert binary input to ASCII hex +# $1 binary value +# return ASCII hex representation +sub bin2hex($) { + my $in = shift; + my $len = length($in)*2; + return unpack("H$len", "$in"); +} + +# $1: binary byte (character) +# returns: binary byte with odd parity using low bit as parity bit +sub odd_par($) { + my $in = ord(shift); + my $odd_count=0; + for(my $i=1; $i<8; $i++) { + $odd_count++ if ($in & (1<<$i)); + } + + my $out = $in; + if ($odd_count & 1) { # check if parity is already odd + $out &= ~1; # clear the low bit + } else { + $out |= 1; # set the low bit + } + + return chr($out); +} + +# DES keys uses only the 7 high bits of a byte, the 8th low bit +# is the parity bit +# as the new key is calculated from oldkey XOR cipher in the MCT test, +# the parity is not really checked and needs to be set to match +# expectation (OpenSSL does not really care, but the FIPS +# test result is expected that the key has the appropriate parity) +# $1: arbitrary binary string +# returns: string with odd parity set in low bit of each byte +sub fix_key_parity($) { + my $in = shift; + my $out = ""; + for (my $i = 0; $i < length($in); $i++) { + $out .= odd_par(substr($in, $i, 1)); + } + + return $out; +} + +#################################################### +# DER/PEM utility functions +# Cf. http://www.columbia.edu/~ariel/ssleay/layman.html + +# Convert unsigned integer to base256 bigint bytes +# $1 integer +# returns base256 octet string +sub int_base256_unsigned($) { + my $n = shift; + + my $out = chr($n & 255); + while ($n>>=8) { + $out = chr($n & 255) . $out; + } + + return $out; +} + +# Convert signed integer to base256 bigint bytes +# $1 integer +# returns base256 octet string +sub int_base256_signed($) { + my $n = shift; + my $negative = ($n < 0); + + if ($negative) { + $n = -$n-1; + } + + my $out = int_base256_unsigned($n); + + if (ord(substr($out, 0, 1)) & 128) { + # it's supposed to be positive but has sign bit set, + # add a leading zero + $out = chr(0) . $out; + } + + if ($negative) { + my $neg = chr(255) x length($out); + $out ^= $neg; + } + + return $out; +} + +# Length header for specified DER object length +# $1 length as integer +# return octet encoding for length +sub der_len($) { + my $len = shift; + + if ($len <= 127) { + return chr($len); + } else { + my $blen = int_base256_unsigned($len); + + return chr(128 | length($blen)) . $blen; + } +} + +# Prepend length header to object +# $1 object as octet sequence +# return length header for object followed by object as octets +sub der_len_obj($) { + my $x = shift; + + return der_len(length($x)) . $x; +} + +# DER sequence +# $* objects +# returns DER sequence consisting of the objects passed as arguments +sub der_seq { + my $seq = join("", @_); + return chr(0x30) . der_len_obj($seq); +} + +# DER bitstring +# $1 input octets (must be full octets, fractional octets not supported) +# returns input encapsulated as bitstring +sub der_bitstring($) { + my $x = shift; + + $x = chr(0) . $x; + + return chr(0x03) . der_len_obj($x); +} + +# base-128-encoded integer, used for object numbers. +# $1 integer +# returns octet sequence +sub der_base128($) { + my $n = shift; + + my $out = chr($n & 127); + + while ($n>>=7) { + $out = chr(128 | ($n & 127)) . $out; + } + + return $out; +} + +# Generating the PEM certificate string +# (base-64-encoded DER string) +# $1 DER string +# returns octet sequence +sub pem_cert($) { + my $n = shift; + + my $out = "-----BEGIN PUBLIC KEY-----\n"; + $out .= encode_base64($n); + $out .= "-----END PUBLIC KEY-----\n"; + + return $out; +} + +# DER object identifier +# $* sequence of id numbers +# returns octets +sub der_objectid { + my $v1 = shift; + my $v2 = shift; + + my $out = chr(40*$v1 + $v2) . join("", map { der_base128($_) } @_); + + return chr(0x06) . der_len_obj($out); +} + +# DER signed integer +# $1 number as octet string (base 256 representation, high byte first) +# returns number in DER integer encoding +sub der_bigint($) { + my $x = shift; + + return chr(0x02) . der_len_obj($x); +} + +# DER positive integer with leading zeroes stripped +# $1 number as octet string (base 256 representation, high byte first) +# returns number in DER integer encoding +sub der_pos_bigint($) { + my $x = shift; + + # strip leading zero digits + $x =~ s/^[\0]+//; + + # need to prepend a zero if high bit set, since it would otherwise be + # interpreted as a negative number. Also needed for number 0. + if (!length($x) || ord(substr($x, 0, 1)) >= 128) { + $x = chr(0) . $x; + } + + return der_bigint($x); +} + +# $1 number as signed integer +# returns number as signed DER integer encoding +sub der_int($) { + my $n = shift; + + return der_bigint(int_base256_signed($n)); +} + +# the NULL object constant +sub der_null() { + return chr(0x05) . chr(0x00); +} + +# Unit test helper +# $1 calculated result +# $2 expected result +# no return value, dies if results differ, showing caller's line number +sub der_test($$) { + my $actual = bin2hex(shift); + my $expected = shift; + + my @caller = caller; + $actual eq $expected or die "Error:line $caller[2]:assertion failed: " + ."$actual != $expected\n"; +} + +# Unit testing for the DER encoding functions +# Examples from http://www.columbia.edu/~ariel/ssleay/layman.html +# No input, no output. Dies if unit tests fail. +sub der_unit_test { + ## uncomment these if you want to test the test framework + #print STDERR "Unit test running\n"; + #der_test chr(0), "42"; + + der_test der_null, "0500"; + + # length bytes + der_test der_len(1), "01"; + der_test der_len(127), "7f"; + der_test der_len(128), "8180"; + der_test der_len(256), "820100"; + der_test der_len(65536), "83010000"; + + # bigint + der_test der_bigint(chr(0)), "020100"; + der_test der_bigint(chr(128)), "020180"; # -128 + der_test der_pos_bigint(chr(128)), "02020080"; # +128 + der_test der_pos_bigint(chr(0).chr(0).chr(1)), "020101"; + der_test der_pos_bigint(chr(0)), "020100"; + + # integers (tests base256 conversion) + der_test der_int( 0), "020100"; + der_test der_int( 127), "02017f"; + der_test der_int( 128), "02020080"; + der_test der_int( 256), "02020100"; + der_test der_int( -1), "0201ff"; + der_test der_int( -128), "020180"; + der_test der_int( -129), "0202ff7f"; + der_test der_int(-65536), "0203ff0000"; + der_test der_int(-65537), "0203feffff"; + + # object encoding, "RSA Security" + der_test der_base128(840), "8648"; + der_test der_objectid(1, 2, 840, 113549), "06062a864886f70d"; + + # Combinations + der_test der_bitstring("ABCD"), "03050041424344"; + der_test der_bitstring(der_null), "0303000500"; + der_test der_seq(der_int(0), der_null), "30050201000500"; + + # The big picture + der_test der_seq(der_seq(der_objectid(1, 2, 840, 113549), der_null), + der_bitstring(der_seq(der_pos_bigint(chr(5)), + der_pos_bigint(chr(3))))), + "3017300a06062a864886f70d05000309003006020105020103"; +} + +#################################################### +# OpenSSL missing functionality workarounds + +## Format of an RSA public key: +# 0:d=0 hl=3 l= 159 cons: SEQUENCE +# 3:d=1 hl=2 l= 13 cons: SEQUENCE +# 5:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption +# 16:d=2 hl=2 l= 0 prim: NULL +# 18:d=1 hl=3 l= 141 prim: BIT STRING +# [ sequence: INTEGER (n), INTEGER (e) ] + +# generate RSA pub key in PEM format +# $1: filename where PEM key is to be stored +# $2: n of the RSA key in hex +# $3: e of the RSA key in hex +# return: nothing, but file created +sub gen_pubrsakey($$$) { + my $filename=shift; + my $n = shift; + my $e = shift; + + # make sure the DER encoder works ;-) + der_unit_test(); + + # generate DER encoding of the public key + + my $rsaEncryption = der_objectid(1, 2, 840, 113549, 1, 1, 1); + + my $der = der_seq(der_seq($rsaEncryption, der_null), + der_bitstring(der_seq(der_pos_bigint(hex2bin($n)), + der_pos_bigint(hex2bin($e))))); + + open(FH, ">", $filename) or die; + print FH pem_cert($der); + close FH; + +} + +# generate RSA pub key in PEM format +# +# This implementation uses "openssl asn1parse -genconf" which was added +# in openssl 0.9.8. It is not available in older openssl versions. +# +# $1: filename where PEM key is to be stored +# $2: n of the RSA key in hex +# $3: e of the RSA key in hex +# return: nothing, but file created +sub gen_pubrsakey_using_openssl($$$) { + my $filename=shift; + my $n = shift; + my $e = shift; + + my $asn1 = "asn1=SEQUENCE:pubkeyinfo + +[pubkeyinfo] +algorithm=SEQUENCE:rsa_alg +pubkey=BITWRAP,SEQUENCE:rsapubkey + +[rsa_alg] +algorithm=OID:rsaEncryption +parameter=NULL + +[rsapubkey] +n=INTEGER:0x$n + +e=INTEGER:0x$e"; + + open(FH, ">$filename.cnf") or die "Cannot create file $filename.cnf: $?"; + print FH $asn1; + close FH; + my @args = ("openssl", "asn1parse", "-genconf", "$filename.cnf", "-noout", "-out", "$filename.der"); + system(@args) == 0 or die "system @args failed: $?"; + @args = ("openssl", "rsa", "-inform", "DER", "-in", "$filename.der", + "-outform", "PEM", "-pubin", "-pubout", "-out", "$filename"); + system(@args) == 0 or die "system @args failed: $?"; + die "RSA PEM formatted key file $filename was not created" + if (! -f $filename); + + unlink("$filename.cnf"); + unlink("$filename.der"); +} + +############################################ +# Test cases + +# This is the Known Answer Test +# $1: the string that we have to put in front of the key +# when printing the key +# $2: crypto key1 in hex form +# $3: crypto key2 in hex form (TDES, undef otherwise) +# $4: crypto key3 in hex form (TDES, undef otherwise) +# $5: IV in hex form +# $6: Plaintext (enc=1) or Ciphertext (enc=0) in hex form +# $7: cipher +# $8: encrypt=1/decrypt=0 +# return: string formatted as expected by CAVS +sub kat($$$$$$$$) { + my $keytype = shift; + my $key1 = shift; + my $key2 = shift; + my $key3 = shift; + my $iv = shift; + my $pt = shift; + my $cipher = shift; + my $enc = shift; + + my $out = ""; + + $out .= "$keytype = $key1\n"; + + # this is the concardination of the keys for 3DES + if (defined($key2)) { + $out .= "KEY2 = $key2\n"; + $key1 = $key1 . $key2; + } + if (defined($key3)) { + $out .= "KEY3 = $key3\n"; + $key1= $key1 . $key3; + } + + $out .= "IV = $iv\n" if (defined($iv) && $iv ne ""); + if ($enc) { + $out .= "PLAINTEXT = $pt\n"; + $out .= "CIPHERTEXT = " . &$encdec($key1, $iv, $cipher, 1, $pt) . "\n"; + } else { + $out .= "CIPHERTEXT = $pt\n"; + $out .= "PLAINTEXT = " . &$encdec($key1, $iv, $cipher, 0, $pt) . "\n"; + } + + return $out; +} + +# This is the Known Answer Test for Hashes +# $1: Plaintext in hex form +# $2: hash +# $3: hash length (undef if not applicable) +# return: string formatted as expected by CAVS +sub hash_kat($$$) { + my $pt = shift; + my $cipher = shift; + my $len = shift; + + my $out = ""; + $out .= "Len = $len\n" if (defined($len)); + $out .= "Msg = $pt\n"; + + $pt = "" if(!$len); + $out .= "MD = " . &$hash($pt, $cipher) . "\n"; + return $out; +} + +# Known Answer Test for HMAC hash +# $1: key length in bytes +# $2: MAC length in bytes +# $3: key for HMAC in hex form +# $4: message to be hashed +# return: string formatted as expected by CAVS +sub hmac_kat($$$$) { + my $klen = shift; + my $tlen = shift; + my $key = shift; + my $msg = shift; + + # XXX this is a hack - we need to decipher the HMAC REQ files in a more + # sane way + # + # This is a conversion table from the expected hash output size + # to the assumed hash type - we only define here the block size of + # the underlying hashes and do not allow any truncation + my %hashtype = ( + 20 => 1, + 28 => 224, + 32 => 256, + 48 => 384, + 64 => 512 + ); + + die "Hash output size $tlen is not supported!" + if(!defined($hashtype{$tlen})); + + my $out = ""; + $out .= "Klen = $klen\n"; + $out .= "Tlen = $tlen\n"; + $out .= "Key = $key\n"; + $out .= "Msg = $msg\n"; + $out .= "Mac = " . &$hmac($key, $tlen, $msg, $hashtype{$tlen}) . "\n"; + + return $out; +} + + +# Cipher Monte Carlo Testing +# $1: the string that we have to put in front of the key +# when printing the key +# $2: crypto key1 in hex form +# $3: crypto key2 in hex form (TDES, undef otherwise) +# $4: crypto key3 in hex form (TDES, undef otherwise) +# $5: IV in hex form +# $6: Plaintext (enc=1) or Ciphertext (enc=0) in hex form +# $7: cipher +# $8: encrypt=1/decrypt=0 +# return: string formatted as expected by CAVS +sub crypto_mct($$$$$$$$) { + my $keytype = shift; + my $key1 = hex2bin(shift); + my $key2 = shift; + my $key3 = shift; + my $iv = hex2bin(shift); + my $source_data = hex2bin(shift); + my $cipher = shift; + my $enc = shift; + + my $out = ""; + + $key2 = hex2bin($key2) if (defined($key2)); + $key3 = hex2bin($key3) if (defined($key3)); + my $bufsize = length($source_data); + + # for AES: outer loop 0-99, inner 0-999 based on FIPS compliance tests + # for RC4: outer loop 0-99, inner 0-999 based on atsec compliance tests + # for DES: outer loop 0-399, inner 0-9999 based on FIPS compliance tests + my $ciph = substr($cipher,0,3); + my $oloop=100; + my $iloop=1000; + if ($ciph =~ /des/) {$oloop=400;$iloop=10000;} + + for (my $i=0; $i<$oloop; ++$i) { + $out .= "COUNT = $i\n"; + if (defined($key2)) { + $out .= "$keytype = ". bin2hex($key1). "\n"; + $out .= "KEY2 = ". bin2hex($key2). "\n"; + $key1 = $key1 . $key2; + } else { + $out .= "$keytype = ". bin2hex($key1). "\n"; + } + if(defined($key3)) { + $out .= "KEY3 = ". bin2hex($key3). "\n"; + $key1 = $key1 . $key3; + } + my $keylen = length($key1); + + $out .= "IV = ". bin2hex($iv) . "\n" + if (defined($iv) && $iv ne ""); + + if ($enc) { + $out .= "PLAINTEXT = ". bin2hex($source_data). "\n"; + } else { + $out .= "CIPHERTEXT = ". bin2hex($source_data). "\n"; + } + my ($CO, $CI); + my $cipher_imp = &$state_cipher($cipher, $enc, $bufsize, $key1, $iv); + $cipher_imp = &$state_cipher_des($cipher, $enc, $bufsize, $key1, $iv) if($cipher =~ /des/); + my $pid = open2($CO, $CI, $cipher_imp); + + my $calc_data = $iv; # CT[j] + my $old_calc_data; # CT[j-1] + my $old_old_calc_data; # CT[j-2] + my $next_source; + + # TDES inner loop implements logic within driver + if ($cipher =~ /des/) { + # Need to provide a dummy IV in case of ECB mode. + my $iv_arg = (defined($iv) && $iv ne "") + ? bin2hex($iv) + : "00"x(length($source_data)); + print $CI "1\n" + .$iloop."\n" + .bin2hex($key1)."\n" + .$iv_arg."\n" + .bin2hex($source_data)."\n\n" or die; + chomp(my $line = <$CO>); + $calc_data = hex2bin($line); + chomp($line = <$CO>); + $old_calc_data = hex2bin($line); + chomp($line = <$CO>); + $old_old_calc_data = hex2bin($line); + chomp($line = <$CO>); + $iv = hex2bin($line) if (defined($iv) && $iv ne ""); + chomp($line = <$CO>); + $next_source = hex2bin($line); + # Skip over empty line. + $line = <$CO>; + } else { + for (my $j = 0; $j < $iloop; ++$j) { + $old_old_calc_data = $old_calc_data; + $old_calc_data = $calc_data; + + #print STDERR "source_data=", bin2hex($source_data), "\n"; + syswrite $CI, $source_data or die $!; + my $len = sysread $CO, $calc_data, $bufsize; + + #print STDERR "len=$len, bufsize=$bufsize\n"; + die if $len ne $bufsize; + #print STDERR "calc_data=", bin2hex($calc_data), "\n"; + + if ( (!$enc && $ciph =~ /des/) || + $ciph =~ /rc4/ || + $cipher =~ /ecb/ ) { + #TDES in decryption mode, RC4 and ECB mode + #have a special rule + $source_data = $calc_data; + } else { + $source_data = $old_calc_data; + } + } + } + close $CO; + close $CI; + waitpid $pid, 0; + + if ($enc) { + $out .= "CIPHERTEXT = ". bin2hex($calc_data). "\n\n"; + } else { + $out .= "PLAINTEXT = ". bin2hex($calc_data). "\n\n"; + } + + if ( $ciph =~ /aes/ ) { + $key1 ^= substr($old_calc_data . $calc_data, -$keylen); + #print STDERR bin2hex($key1)."\n"; + } elsif ( $ciph =~ /des/ ) { + die "Wrong keylen $keylen" if ($keylen != 24); + + # $nkey needed as $key holds the concatenation of the + # old key atm + my $nkey = fix_key_parity(substr($key1,0,8) ^ $calc_data); + #print STDERR "KEY1 = ". bin2hex($nkey)."\n"; + if (substr($key1,0,8) ne substr($key1,8,8)) { + #print STDERR "KEY2 recalc: KEY1==KEY3, KEY2 indep. or all KEYs are indep.\n"; + $key2 = fix_key_parity((substr($key1,8,8) ^ $old_calc_data)); + } else { + #print STDERR "KEY2 recalc: KEY1==KEY2==KEY3\n"; + $key2 = fix_key_parity((substr($key1,8,8) ^ $calc_data)); + } + #print STDERR "KEY2 = ". bin2hex($key2)."\n"; + if ( substr($key1,0,8) eq substr($key1,16)) { + #print STDERR "KEY3 recalc: KEY1==KEY2==KEY3 or KEY1==KEY3, KEY2 indep.\n"; + $key3 = fix_key_parity((substr($key1,16) ^ $calc_data)); + } else { + #print STDERR "KEY3 recalc: all KEYs are independent\n"; + $key3 = fix_key_parity((substr($key1,16) ^ $old_old_calc_data)); + } + #print STDERR "KEY3 = ". bin2hex($key3)."\n"; + + # reset the first key - concardination happens at + # beginning of loop + $key1=$nkey; + } elsif ($ciph =~ /rc4/ ) { + $key1 ^= substr($calc_data, 0, 16); + #print STDERR bin2hex($key1)."\n"; + } else { + die "Test limitation: cipher '$cipher' not supported in Monte Carlo testing"; + } + + if ($cipher =~ /des-ede3-ofb/) { + $source_data = $source_data ^ $next_source; + } elsif (!$enc && $cipher =~ /des-ede3-cfb/) { + #TDES decryption CFB has a special rule + $source_data = $next_source; + } elsif ( $ciph =~ /rc4/ || $cipher eq "des-ede3" || $cipher =~ /ecb/) { + #No resetting of IV as the IV is all zero set initially (i.e. no IV) + $source_data = $calc_data; + } elsif (! $enc && $ciph =~ /des/ ) { + #TDES in decryption mode has a special rule + $iv = $old_calc_data; + $source_data = $calc_data; + } else { + $iv = $calc_data; + $source_data = $old_calc_data; + } + } + + return $out; +} + +# Hash Monte Carlo Testing +# $1: Plaintext in hex form +# $2: hash +# return: string formatted as expected by CAVS +sub hash_mct($$) { + my $pt = shift; + my $cipher = shift; + + my $out = ""; + + $out .= "Seed = $pt\n\n"; + + for (my $j=0; $j<100; ++$j) { + $out .= "COUNT = $j\n"; + my $md0=$pt; + my $md1=$pt; + my $md2=$pt; + for (my $i=0; $i<1000; ++$i) { + #print STDERR "outer loop $j; inner loop $i\n"; + my $mi= $md0 . $md1 . $md2; + $md0=$md1; + $md1=$md2; + $md2 = &$hash($mi, $cipher); + $md2 =~ s/\n//; + } + $out .= "MD = $md2\n\n"; + $pt=$md2; + } + + return $out; +} + +# RSA SigGen test +# $1: Message to be signed in hex form +# $2: Hash algorithm +# $3: file name with RSA key in PEM form +# return: string formatted as expected by CAVS +sub rsa_siggen($$$) { + my $data = shift; + my $cipher = shift; + my $keyfile = shift; + + my $out = ""; + + $out .= "SHAAlg = $cipher\n"; + $out .= "Msg = $data\n"; + $out .= "S = " . &$rsa_sign($data, lc($cipher), $keyfile) . "\n"; + + return $out; +} + +# RSA SigVer test +# $1: Message to be verified in hex form +# $2: Hash algorithm +# $3: Signature of message in hex form +# $4: n of the RSA key in hex in hex form +# $5: e of the RSA key in hex in hex form +# return: string formatted as expected by CAVS +sub rsa_sigver($$$$$) { + my $data = shift; + my $cipher = shift; + my $signature = shift; + my $n = shift; + my $e = shift; + + my $out = ""; + + $out .= "SHAAlg = $cipher\n"; + $out .= "e = $e\n"; + $out .= "Msg = $data\n"; + $out .= "S = $signature\n"; + + # XXX maybe a secure temp file name is better here + # but since it is not run on a security sensitive + # system, I hope that this is fine + my $keyfile = "rsa_sigver.tmp.$$"; + gen_pubrsakey($keyfile, $n, $e); + + my $sigfile = "$keyfile.sig"; + open(FH, ">$sigfile") or die "Cannot create file $sigfile: $?"; + print FH hex2bin($signature); + close FH; + + $out .= "Result = " . (&$rsa_verify($data, lc($cipher), $keyfile, $sigfile) ? "P\n" : "F\n"); + + unlink($keyfile); + unlink($sigfile); + + return $out; +} + +# RSA X9.31 key generation test +# $1 modulus size +# $2 e +# $3 xp1 +# $4 xp2 +# $5 Xp +# $6 xq1 +# $7 xq2 +# $8 Xq +# return: string formatted as expected by CAVS +sub rsa_keygen($$$$$$$$) { + my $modulus = shift; + my $e = shift; + my $xp1 = shift; + my $xp2 = shift; + my $Xp = shift; + my $xq1 = shift; + my $xq2 = shift; + my $Xq = shift; + + my $out = ""; + + my $ret = &$rsa_derive($modulus, $e, $xp1, $xp2, $Xp, $xq1, $xq2, $Xq); + + my ($P, $Q, $N, $D) = split(/\n/, $ret); + + $out .= "e = $e\n"; + $out .= "xp1 = $xp1\n"; + $out .= "xp2 = $xp2\n"; + $out .= "Xp = $Xp\n"; + $out .= "p = $P\n"; + $out .= "xq1 = $xq1\n"; + $out .= "xq2 = $xq2\n"; + $out .= "Xq = $Xq\n"; + $out .= "q = $Q\n"; + $out .= "n = $N\n"; + $out .= "d = $D\n\n"; + + return $out; + +} + +# X9.31 RNG test +# $1 key for the AES cipher +# $2 DT value +# $3 V value +# $4 type ("VST", "MCT") +# return: string formatted as expected by CAVS +sub rngx931($$$$) { + my $key=shift; + my $dt=shift; + my $v=shift; + my $type=shift; + + my $out = "Key = $key\n"; + $out .= "DT = $dt\n"; + $out .= "V = $v\n"; + + my $count = 1; + $count = 10000 if ($type eq "MCT"); + + my $rnd_val = ""; + + # we read 16 bytes from RNG + my $bufsize = 16; + + my ($CO, $CI); + my $rng_imp = &$state_rng($key, $dt, $v); + my $pid = open2($CO, $CI, $rng_imp); + for (my $i = 0; $i < $count; ++$i) { + my $len = sysread $CO, $rnd_val, $bufsize; + #print STDERR "len=$len, bufsize=$bufsize\n"; + die "len=$len != bufsize=$bufsize" if $len ne $bufsize; + #print STDERR "calc_data=", bin2hex($rnd_val), "\n"; + } + close $CO; + close $CI; + waitpid $pid, 0; + + $out .= "R = " . bin2hex($rnd_val) . "\n\n"; + + return $out; +} + +# DSA PQGGen test +# $1 modulus size +# $2 number of rounds to perform the test +# return: string formatted as expected by CAVS +sub dsa_pqggen_driver($$) { + my $mod = shift; + my $rounds = shift; + + my $out = ""; + for(my $i=0; $i<$rounds; $i++) { + my $ret = &$dsa_pqggen($mod); + my ($P, $Q, $G, $Seed, $c, $H) = split(/\n/, $ret); + die "Return value does not contain all expected values of P, Q, G, Seed, c, H for dsa_pqggen" + if (!defined($P) || !defined($Q) || !defined($G) || + !defined($Seed) || !defined($c) || !defined($H)); + + # now change the counter to decimal as CAVS wants decimal + # counter value although all other is HEX + $c = hex($c); + + $out .= "P = $P\n"; + $out .= "Q = $Q\n"; + $out .= "G = $G\n"; + $out .= "Seed = $Seed\n"; + $out .= "c = $c\n"; + $out .= "H = $H\n\n"; + } + + return $out; +} + + +# DSA SigGen test +# $1: Message to be signed in hex form +# $2: file name with DSA key in PEM form +# return: string formatted as expected by CAVS +sub dsa_siggen($$) { + my $data = shift; + my $keyfile = shift; + + my $out = ""; + + my %ret = &$dsa_sign($data, $keyfile); + + $out .= "Msg = $data\n"; + $out .= "Y = " . $ret{'Y'} . "\n"; + $out .= "R = " . $ret{'R'} . "\n"; + $out .= "S = " . $ret{'S'} . "\n"; + + return $out; +} + + +# DSA signature verification +# $1 modulus +# $2 P +# $3 Q +# $4 G +# $5 Y - public key +# $6 r +# $7 s +# $8 message to be verified +# return: string formatted as expected by CAVS +sub dsa_sigver($$$$$$$$) { + my $modulus = shift; + my $p = shift; + my $q = shift; + my $g = shift; + my $y = shift; + my $r = shift; + my $s = shift; + my $msg = shift; + + my $out = ""; + + #PQG are already printed - do not print them here + + $out .= "Msg = $msg\n"; + $out .= "Y = $y\n"; + $out .= "R = $r\n"; + $out .= "S = $s\n"; + + # XXX maybe a secure temp file name is better here + # but since it is not run on a security sensitive + # system, I hope that this is fine + my $keyfile = "dsa_sigver.tmp.$$"; + &$dsa_genpubkey($keyfile, $p, $q, $g, $y); + + $out .= "Result = " . (&$dsa_verify($msg, $keyfile, $r, $s) ? "P\n" : "F\n"); + + unlink($keyfile); + + return $out; +} + +############################################################## +# Parser of input file and generator of result file +# + +sub usage() { + + print STDERR "Usage: +$0 [-R] [-D] [-I name] + +-R execution of ARCFOUR instead of OpenSSL +-I NAME Use interface style NAME: + openssl OpenSSL (default) + libgcrypt Libgcrypt +-D SigGen and SigVer are executed with DSA + Please note that the DSA CAVS vectors do not allow distinguishing + them from the RSA vectors. As the RSA test is the default, you have + to supply this option to apply the DSA logic"; +} + +# Parser of CAVS test vector file +# $1: Test vector file +# $2: Output file for test results +# return: nothing +sub parse($$) { + my $infile = shift; + my $outfile = shift; + + my $out = ""; + + # this is my cipher/hash type + my $cipher = ""; + + # Test type + # 1 - cipher known answer test + # 2 - cipher Monte Carlo test + # 3 - hash known answer test + # 4 - hash Monte Carlo test + # 5 - RSA signature generation + # 6 - RSA signature verification + my $tt = 0; + + # Variables for tests + my $keytype = ""; # we can have "KEY", "KEYs", "KEY1" + my $key1 = ""; + my $key2 = undef; #undef needed for allowing + my $key3 = undef; #the use of them as input variables + my $pt = ""; + my $enc = 1; + my $iv = ""; + my $len = undef; #see key2|3 + my $n = ""; + my $e = ""; + my $signature = ""; + my $rsa_keyfile = ""; + my $dsa_keyfile = ""; + my $dt = ""; + my $v = ""; + my $klen = ""; + my $tlen = ""; + my $modulus = ""; + my $capital_n = 0; + my $capital_p = ""; + my $capital_q = ""; + my $capital_g = ""; + my $capital_y = ""; + my $capital_r = ""; + my $xp1 = ""; + my $xp2 = ""; + my $Xp = ""; + my $xq1 = ""; + my $xq2 = ""; + my $Xq = ""; + + my $mode = ""; + + open(IN, "<$infile"); + while() { + + my $line = $_; + chomp($line); + $line =~ s/\r//; + + my $keylen = ""; + + # Mode and type check + # consider the following parsed line + # '# AESVS MCT test data for CBC' + # '# TDES Multi block Message Test for CBC' + # '# INVERSE PERMUTATION - KAT for CBC' + # '# SUBSTITUTION TABLE - KAT for CBC' + # '# TDES Monte Carlo (Modes) Test for CBC' + # '# "SHA-1 Monte" information for "IBMRHEL5"' + # '# "SigVer PKCS#1 Ver 1.5" information for "IBMRHEL5"' + # '# "SigGen PKCS#1 Ver 1.5" information for "IBMRHEL5"' + # '#RC4VS MCT test data' + + # avoid false positives from user specified 'for "PRODUCT"' strings + my $tmpline = $line; + $tmpline =~ s/ for ".*"//; + + ##### Extract cipher + # XXX there may be more - to be added + if ($tmpline =~ /^#.*(CBC|ECB|OFB|CFB|SHA-|SigGen|SigVer|RC4VS|ANSI X9\.31|Hash sizes tested|PQGGen|KeyGen RSA)/) { + if ($tmpline =~ /CBC/) { $mode="cbc"; } + elsif ($tmpline =~ /ECB/) { $mode="ecb"; } + elsif ($tmpline =~ /OFB/) { $mode="ofb"; } + elsif ($tmpline =~ /CFB/) { $mode="cfb"; } + #we do not need mode as the cipher is already clear + elsif ($tmpline =~ /SHA-1/) { $cipher="sha1"; } + elsif ($tmpline =~ /SHA-224/) { $cipher="sha224"; } + elsif ($tmpline =~ /SHA-256/) { $cipher="sha256"; } + elsif ($tmpline =~ /SHA-384/) { $cipher="sha384"; } + elsif ($tmpline =~ /SHA-512/) { $cipher="sha512"; } + #we do not need mode as the cipher is already clear + elsif ($tmpline =~ /RC4VS/) { $cipher="rc4"; } + elsif ($tmpline =~ /SigGen|SigVer/) { + die "Error: X9.31 is not supported" + if ($tmpline =~ /X9/); + $cipher="sha1"; #place holder - might be overwritten later + } + + if ($tmpline =~ /^#.*AESVS/) { + # AES cipher (part of it) + $cipher="aes"; + } + if ($tmpline =~ /^#.*(TDES|KAT)/) { + # TDES cipher (full definition) + # the FIPS-140 test generator tool does not produce + # machine readable output! + if ($mode eq "cbc") { $cipher="des-ede3-cbc"; } + if ($mode eq "ecb") { $cipher="des-ede3"; } + if ($mode eq "ofb") { $cipher="des-ede3-ofb"; } + if ($mode eq "cfb") { $cipher="des-ede3-cfb"; } + } + + # check for RNG + if ($tmpline =~ /ANSI X9\.31/) { + # change the tmpline to add the type of the + # test which is ONLY visible from the file + # name :-( + if ($infile =~ /MCT\.req/) { + $tmpline .= " MCT"; + } elsif ($infile =~ /VST\.req/) { + $tmpline .= " VST"; + } else { + die "Unexpected cipher type with $infile"; + } + } + + if ($tt == 0) { + ##### Identify the test type + if ($tmpline =~ /KeyGen RSA \(X9\.31\)/) { + $tt = 13; + die "Interface function rsa_derive for RSA key generation not defined for tested library" + if (!defined($rsa_derive)); + } elsif ($tmpline =~ /SigVer/ && $opt{'D'} ) { + $tt = 12; + die "Interface function dsa_verify or dsa_genpubkey for DSA verification not defined for tested library" + if (!defined($dsa_verify) || !defined($dsa_genpubkey)); + } elsif ($tmpline =~ /SigGen/ && $opt{'D'}) { + $tt = 11; + die "Interface function dsa_sign or gen_dsakey for DSA sign not defined for tested library" + if (!defined($dsa_sign) || !defined($gen_rsakey)); + } elsif ($tmpline =~ /PQGGen/) { + $tt = 10; + die "Interface function for DSA PQGGen testing not defined for tested library" + if (!defined($dsa_pqggen)); + } elsif ($tmpline =~ /Hash sizes tested/) { + $tt = 9; + die "Interface function hmac for HMAC testing not defined for tested library" + if (!defined($hmac)); + } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /MCT/) { + $tt = 8; + die "Interface function state_rng for RNG MCT not defined for tested library" + if (!defined($state_rng)); + } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /VST/) { + $tt = 7; + die "Interface function state_rng for RNG KAT not defined for tested library" + if (!defined($state_rng)); + } elsif ($tmpline =~ /SigVer/ ) { + $tt = 6; + die "Interface function rsa_verify or gen_rsakey for RSA verification not defined for tested library" + if (!defined($rsa_verify) || !defined($gen_rsakey)); + } elsif ($tmpline =~ /SigGen/ ) { + $tt = 5; + die "Interface function rsa_sign or gen_rsakey for RSA sign not defined for tested library" + if (!defined($rsa_sign) || !defined($gen_rsakey)); + } elsif ($tmpline =~ /Monte|MCT|Carlo/ && $cipher =~ /^sha/) { + $tt = 4; + die "Interface function hash for Hashing not defined for tested library" + if (!defined($hash)); + } elsif ($tmpline =~ /Monte|MCT|Carlo/) { + $tt = 2; + die "Interface function state_cipher for Stateful Cipher operation defined for tested library" + if (!defined($state_cipher) || !defined($state_cipher_des)); + } elsif ($cipher =~ /^sha/) { + $tt = 3; + die "Interface function hash for Hashing not defined for tested library" + if (!defined($hash)); + } else { + $tt = 1; + die "Interface function encdec for Encryption/Decryption not defined for tested library" + if (!defined($encdec)); + } + } + } + + # This is needed as ARCFOUR does not operate with an IV + $iv = "00000000000000000000000000000000" if ($cipher eq "rc4" + && $iv eq "" ); + + # we are now looking for the string + # '# Key Length : 256' + # found in AES + if ($tmpline =~ /^# Key Length.*?(128|192|256)/) { + if ($cipher eq "aes") { + $cipher="$cipher-$1-$mode"; + } else { + die "Error: Key length $1 given for cipher $cipher which is unexpected"; + } + } + + # Get the test data + if ($line =~ /^(KEY|KEY1|Key)\s*=\s*(.*)/) { # found in ciphers and RNG + die "KEY seen twice - input file crap" if ($key1 ne ""); + $keytype=$1; + $key1=$2; + $key1 =~ s/\s//g; #replace potential white spaces + } + elsif ($line =~ /^(KEYs)\s*=\s*(.*)/) { # found in ciphers and RNG + die "KEY seen twice - input file crap" if ($key1 ne ""); + $keytype=$1; + $key1=$2; + $key1 =~ s/\s//g; #replace potential white spaces + $key2 = $key1; + $key3 = $key1; + } + elsif ($line =~ /^KEY2\s*=\s*(.*)/) { # found in TDES + die "First key not set, but got already second key - input file crap" if ($key1 eq ""); + die "KEY2 seen twice - input file crap" if (defined($key2)); + $key2=$1; + $key2 =~ s/\s//g; #replace potential white spaces + } + elsif ($line =~ /^KEY3\s*=\s*(.*)/) { # found in TDES + die "Second key not set, but got already third key - input file crap" if ($key2 eq ""); + die "KEY3 seen twice - input file crap" if (defined($key3)); + $key3=$1; + $key3 =~ s/\s//g; #replace potential white spaces + } + elsif ($line =~ /^IV\s*=\s*(.*)/) { # found in ciphers + die "IV seen twice - input file crap" if ($iv ne ""); + $iv=$1; + $iv =~ s/\s//g; #replace potential white spaces + } + elsif ($line =~ /^PLAINTEXT\s*=\s*(.*)/) { # found in ciphers + if ( $1 !~ /\?/ ) { #only use it if there is valid hex data + die "PLAINTEXT/CIPHERTEXT seen twice - input file crap" if ($pt ne ""); + $pt=$1; + $pt =~ s/\s//g; #replace potential white spaces + $enc=1; + } + } + elsif ($line =~ /^CIPHERTEXT\s*=\s*(.*)/) { # found in ciphers + if ( $1 !~ /\?/ ) { #only use it if there is valid hex data + die "PLAINTEXT/CIPHERTEXT seen twice - input file crap" if ($pt ne ""); + $pt=$1; + $pt =~ s/\s//g; #replace potential white spaces + $enc=0; + } + } + elsif ($line =~ /^Len\s*=\s*(.*)/) { # found in hashs + $len=$1; + } + elsif ($line =~ /^(Msg|Seed)\s*=\s*(.*)/) { # found in hashs + die "Msg/Seed seen twice - input file crap" if ($pt ne ""); + $pt=$2; + } + elsif ($line =~ /^\[mod\s*=\s*(.*)\]$/) { # found in RSA requests + $modulus = $1; + $out .= $line . "\n\n"; # print it + # generate the private key with given bit length now + # as we have the required key length in bit + if ($tt == 11) { + $dsa_keyfile = "dsa_siggen.tmp.$$"; + my %pqg = &$gen_dsakey($dsa_keyfile); + $out .= "P = " . $pqg{'P'} . "\n"; + $out .= "Q = " . $pqg{'Q'} . "\n"; + $out .= "G = " . $pqg{'G'} . "\n"; + } elsif ( $tt == 5 ) { + # XXX maybe a secure temp file name is better here + # but since it is not run on a security sensitive + # system, I hope that this is fine + $rsa_keyfile = "rsa_siggen.tmp.$$"; + &$gen_rsakey($modulus, $rsa_keyfile); + my $modulus = pipe_through_program("", "openssl rsa -pubout -modulus -in $rsa_keyfile"); + $modulus =~ s/Modulus=(.*?)\s(.|\s)*/$1/; + $out .= "n = $modulus\n"; + $out .= "\ne = 10001\n" + } + } + elsif ($line =~ /^SHAAlg\s*=\s*(.*)/) { #found in RSA requests + $cipher=$1; + } + elsif($line =~ /^n\s*=\s*(.*)/) { # found in RSA requests + $out .= $line . "\n"; + $n=$1; + } + elsif ($line =~ /^e\s*=\s*(.*)/) { # found in RSA requests + $e=$1; + } + elsif ($line =~ /^S\s*=\s*(.*)/) { # found in RSA requests + die "S seen twice - input file crap" if ($signature ne ""); + $signature=$1; + } + elsif ($line =~ /^DT\s*=\s*(.*)/) { # X9.31 RNG requests + die "DT seen twice - check input file" + if ($dt ne ""); + $dt=$1; + } + elsif ($line =~ /^V\s*=\s*(.*)/) { # X9.31 RNG requests + die "V seen twice - check input file" + if ($v ne ""); + $v=$1; + } + elsif ($line =~ /^Klen\s*=\s*(.*)/) { # HMAC requests + die "Klen seen twice - check input file" + if ($klen ne ""); + $klen=$1; + } + elsif ($line =~ /^Tlen\s*=\s*(.*)/) { # HMAC RNG requests + die "Tlen seen twice - check input file" + if ($tlen ne ""); + $tlen=$1; + } + elsif ($line =~ /^N\s*=\s*(.*)/) { #DSA PQGGen + die "N seen twice - check input file" + if ($capital_n); + $capital_n = $1; + } + elsif ($line =~ /^P\s*=\s*(.*)/) { #DSA SigVer + die "P seen twice - check input file" + if ($capital_p); + $capital_p = $1; + $out .= $line . "\n"; # print it + } + elsif ($line =~ /^Q\s*=\s*(.*)/) { #DSA SigVer + die "Q seen twice - check input file" + if ($capital_q); + $capital_q = $1; + $out .= $line . "\n"; # print it + } + elsif ($line =~ /^G\s*=\s*(.*)/) { #DSA SigVer + die "G seen twice - check input file" + if ($capital_g); + $capital_g = $1; + $out .= $line . "\n"; # print it + } + elsif ($line =~ /^Y\s*=\s*(.*)/) { #DSA SigVer + die "Y seen twice - check input file" + if ($capital_y); + $capital_y = $1; + } + elsif ($line =~ /^R\s*=\s*(.*)/) { #DSA SigVer + die "R seen twice - check input file" + if ($capital_r); + $capital_r = $1; + } + elsif ($line =~ /^xp1\s*=\s*(.*)/) { #RSA key gen + die "xp1 seen twice - check input file" + if ($xp1); + $xp1 = $1; + } + elsif ($line =~ /^xp2\s*=\s*(.*)/) { #RSA key gen + die "xp2 seen twice - check input file" + if ($xp2); + $xp2 = $1; + } + elsif ($line =~ /^Xp\s*=\s*(.*)/) { #RSA key gen + die "Xp seen twice - check input file" + if ($Xp); + $Xp = $1; + } + elsif ($line =~ /^xq1\s*=\s*(.*)/) { #RSA key gen + die "xq1 seen twice - check input file" + if ($xq1); + $xq1 = $1; + } + elsif ($line =~ /^xq2\s*=\s*(.*)/) { #RSA key gen + die "xq2 seen twice - check input file" + if ($xq2); + $xq2 = $1; + } + elsif ($line =~ /^Xq\s*=\s*(.*)/) { #RSA key gen + die "Xq seen twice - check input file" + if ($Xq); + $Xq = $1; + } + else { + $out .= $line . "\n"; + } + + # call tests if all input data is there + if ($tt == 1) { + if ($key1 ne "" && $pt ne "" && $cipher ne "") { + $out .= kat($keytype, $key1, $key2, $key3, $iv, $pt, $cipher, $enc); + $keytype = ""; + $key1 = ""; + $key2 = undef; + $key3 = undef; + $iv = ""; + $pt = ""; + } + } + elsif ($tt == 2) { + if ($key1 ne "" && $pt ne "" && $cipher ne "") { + $out .= crypto_mct($keytype, $key1, $key2, $key3, $iv, $pt, $cipher, $enc); + $keytype = ""; + $key1 = ""; + $key2 = undef; + $key3 = undef; + $iv = ""; + $pt = ""; + } + } + elsif ($tt == 3) { + if ($pt ne "" && $cipher ne "") { + $out .= hash_kat($pt, $cipher, $len); + $pt = ""; + $len = undef; + } + } + elsif ($tt == 4) { + if ($pt ne "" && $cipher ne "") { + $out .= hash_mct($pt, $cipher); + $pt = ""; + } + } + elsif ($tt == 5) { + if ($pt ne "" && $cipher ne "" && $rsa_keyfile ne "") { + $out .= rsa_siggen($pt, $cipher, $rsa_keyfile); + $pt = ""; + } + } + elsif ($tt == 6) { + if ($pt ne "" && $cipher ne "" && $signature ne "" && $n ne "" && $e ne "") { + $out .= rsa_sigver($pt, $cipher, $signature, $n, $e); + $pt = ""; + $signature = ""; + } + } + elsif ($tt == 7 ) { + if ($key1 ne "" && $dt ne "" && $v ne "") { + $out .= rngx931($key1, $dt, $v, "VST"); + $key1 = ""; + $dt = ""; + $v = ""; + } + } + elsif ($tt == 8 ) { + if ($key1 ne "" && $dt ne "" && $v ne "") { + $out .= rngx931($key1, $dt, $v, "MCT"); + $key1 = ""; + $dt = ""; + $v = ""; + } + } + elsif ($tt == 9) { + if ($klen ne "" && $tlen ne "" && $key1 ne "" && $pt ne "") { + $out .= hmac_kat($klen, $tlen, $key1, $pt); + $key1 = ""; + $tlen = ""; + $klen = ""; + $pt = ""; + } + } + elsif ($tt == 10) { + if ($modulus ne "" && $capital_n > 0) { + $out .= dsa_pqggen_driver($modulus, $capital_n); + #$mod is not resetted + $capital_n = 0; + } + } + elsif ($tt == 11) { + if ($pt ne "" && $dsa_keyfile ne "") { + $out .= dsa_siggen($pt, $dsa_keyfile); + $pt = ""; + } + } + elsif ($tt == 12) { + if ($modulus ne "" && + $capital_p ne "" && + $capital_q ne "" && + $capital_g ne "" && + $capital_y ne "" && + $capital_r ne "" && + $signature ne "" && + $pt ne "") { + $out .= dsa_sigver($modulus, + $capital_p, + $capital_q, + $capital_g, + $capital_y, + $capital_r, + $signature, + $pt); + + # We do not clear the domain values PQG and + # the modulus value as they + # are specified only once in a file + # and we do not need to print them as they + # are already printed above + $capital_y = ""; + $capital_r = ""; + $signature = ""; + $pt = ""; + } + } + elsif ($tt == 13) { + if($modulus ne "" && + $e ne "" && + $xp1 ne "" && + $xp2 ne "" && + $Xp ne "" && + $xq1 ne "" && + $xq2 ne "" && + $Xq ne "") { + $out .= rsa_keygen($modulus, + $e, + $xp1, + $xp2, + $Xp, + $xq1, + $xq2, + $Xq); + $e = ""; + $xp1 = ""; + $xp2 = ""; + $Xp = ""; + $xq1 = ""; + $xq2 = ""; + $Xq = ""; + } + } + elsif ($tt > 0) { + die "Test case $tt not defined"; + } + } + + close IN; + $out =~ s/\n/\r\n/g; # make it a dos file + open(OUT, ">$outfile") or die "Cannot create output file $outfile: $?"; + print OUT $out; + close OUT; + +} + +# Signalhandler +sub cleanup() { + unlink("rsa_siggen.tmp.$$"); + unlink("rsa_sigver.tmp.$$"); + unlink("rsa_sigver.tmp.$$.sig"); + unlink("rsa_sigver.tmp.$$.der"); + unlink("rsa_sigver.tmp.$$.cnf"); + unlink("dsa_siggen.tmp.$$"); + unlink("dsa_sigver.tmp.$$"); + unlink("dsa_sigver.tmp.$$.sig"); + exit; +} + +############################################################ +# +# let us pretend to be C :-) +sub main() { + + usage() unless @ARGV; + + getopts("DRI:", \%opt) or die "bad option"; + + ##### Set library + + if ( ! defined $opt{'I'} || $opt{'I'} eq 'openssl' ) { + print STDERR "Using OpenSSL interface functions\n"; + $encdec = \&openssl_encdec; + $rsa_sign = \&openssl_rsa_sign; + $rsa_verify = \&openssl_rsa_verify; + $gen_rsakey = \&openssl_gen_rsakey; + $hash = \&openssl_hash; + $state_cipher = \&openssl_state_cipher; + } elsif ( $opt{'I'} eq 'libgcrypt' ) { + print STDERR "Using libgcrypt interface functions\n"; + $encdec = \&libgcrypt_encdec; + $rsa_sign = \&libgcrypt_rsa_sign; + $rsa_verify = \&libgcrypt_rsa_verify; + $gen_rsakey = \&libgcrypt_gen_rsakey; + $rsa_derive = \&libgcrypt_rsa_derive; + $hash = \&libgcrypt_hash; + $state_cipher = \&libgcrypt_state_cipher; + $state_cipher_des = \&libgcrypt_state_cipher_des; + $state_rng = \&libgcrypt_state_rng; + $hmac = \&libgcrypt_hmac; + $dsa_pqggen = \&libgcrypt_dsa_pqggen; + $gen_dsakey = \&libgcrypt_gen_dsakey; + $dsa_sign = \&libgcrypt_dsa_sign; + $dsa_verify = \&libgcrypt_dsa_verify; + $dsa_genpubkey = \&libgcrypt_dsa_genpubkey; + } else { + die "Invalid interface option given"; + } + + my $infile=$ARGV[0]; + die "Error: Test vector file $infile not found" if (! -f $infile); + + my $outfile = $infile; + # let us add .rsp regardless whether we could strip .req + $outfile =~ s/\.req$//; + if ($opt{'R'}) { + $outfile .= ".rc4"; + } else { + $outfile .= ".rsp"; + } + if (-f $outfile) { + die "Output file $outfile could not be removed: $?" + unless unlink($outfile); + } + print STDERR "Performing tests from source file $infile with results stored in destination file $outfile\n"; + + #Signal handler + $SIG{HUP} = \&cleanup; + $SIG{INT} = \&cleanup; + $SIG{QUIT} = \&cleanup; + $SIG{TERM} = \&cleanup; + + # Do the job + parse($infile, $outfile); + + cleanup(); + +} + +########################################### +# Call it +main(); +1; diff --git a/tests/cavs_tests.sh b/tests/cavs_tests.sh index 7d8499b..75d9601 100755 --- a/tests/cavs_tests.sh +++ b/tests/cavs_tests.sh @@ -55,7 +55,7 @@ function run_one_test () { [ -d "$respdir" ] || mkdir "$respdir" [ -f "$rspfile" ] && rm "$rspfile" - if echo "$reqfile" | grep '/DSA/req/' >/dev/null 2>/dev/null; then + if echo "$reqfile" | grep '/DSA.\?/req/' >/dev/null 2>/dev/null; then dflag="-D" fi diff --git a/tests/cavs_tests.sh.cavs b/tests/cavs_tests.sh.cavs new file mode 100755 index 0000000..7d8499b --- /dev/null +++ b/tests/cavs_tests.sh.cavs @@ -0,0 +1,135 @@ +#!/bin/sh +# Run FIPS CAVS tests +# Copyright 2008 Free Software Foundation, Inc. +# +# This file is free software; as a special exception the author gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Instructions: +# +# 1. Cd to the libgcrypt/tests directory +# +# 2. Unpack the test vector tarball into subdirectory named "cavs". +# An example directory layout after unpacking might be: +# libgcrypt/tests/cavs/AES/req/CBCGFSbox128.req +# libgcrypt/tests/cavs/AES/req/CFB128MCT128.req +# +# Note that below the "cavs" directory there should only be one +# directory part named "req". Further avoid directory part +# names "resp". +# +# 3. Run this script from the libgcrypt/tests directory: +# ./cavs_tests.sh +# +# 4. Send the result file cavs/CAVS_results-*.zip to the testing lab. +# + +# Stop script if something unexpected happens. +set -e + +# A global flag to keep track of errors. +errors_seen_file="$(pwd)/.#cavs_test.errors_seen.tmp" +[ -f "$errors_seen_file" ] && rm "$errors_seen_file" +continue_mode=no +[ "$1" = "--continue" ] && continue_mode=yes + + +# Function to run one test. +# The argument is the request file name. +function run_one_test () { + local reqfile="$1" + local rspfile + local tmprspfile + local respdir + local dflag="" + + tmprspfile=$(echo "$reqfile" | sed 's,.req$,.rsp,') + rspfile=$(echo "$tmprspfile" | sed 's,/req/,/resp/,' ) + respdir=$(dirname "$rspfile") + [ -f "$tmprspfile" ] && rm "$tmprspfile" + [ -d "$respdir" ] || mkdir "$respdir" + [ -f "$rspfile" ] && rm "$rspfile" + + if echo "$reqfile" | grep '/DSA/req/' >/dev/null 2>/dev/null; then + dflag="-D" + fi + + if ./cavs_driver.pl -I libgcrypt $dflag "$reqfile"; then + if [ -f "$tmprspfile" ]; then + mv "$tmprspfile" "$rspfile" + else + echo "failed test: $reqfile" >&2 + : >"$errors_seen_file" + fi + else + echo "failed test: $reqfile rc=$?" >&2 + : >"$errors_seen_file" + fi +} + + + +# Save date and system architecure to construct the output archive name +DATE=$(date +%Y%m%d) +ARCH=$(arch || echo unknown) +result_file="CAVS_results-$ARCH-$DATE.zip" + +for f in fipsdrv cavs_driver.pl; do + if [ ! -f "./$f" ]; then + echo "required program \"$f\" missing in current directory" >&2 + exit 2 + fi +done +if [ ! -d cavs ]; then + echo "required directory \"cavs\" missing below current directory" >&2 + exit 2 +fi +if [ ! zip -h >/dev/null 2>&1 ]; then + echo "required program \"zip\" is not installed on this system" >&2 + exit 2 +fi + +# Set the PATH to this directory so that the perl script is able to +# find the test drivers. +PATH=.:$PATH + +# Check whether there are any stale response files +find cavs -type f -name "*.rsp" | ( while read f ; do + echo "Stale response file: $f" >&2 + any=yes +done +if [ "$any" = "yes" ]; then + echo "Stale response files found" >&2 + if [ "$continue_mode" != "yes" ]; then + echo "use option --continue if that is not a problem" >&2 + exit 1 + fi +fi +) || exit 1 + + +# Find all test files and run the tests. +find cavs -type f -name "*.req" | while read f ; do + echo "Running test file $f" >&2 + run_one_test "$f" + if [ -f "$errors_seen_file" ]; then + break; + fi +done + +if [ -f "$errors_seen_file" ]; then + rm "$errors_seen_file" + echo "Error encountered - not packing up response file" >&2 + exit 1 +fi + +echo "Packing up all response files" >&2 +cd cavs +find . -type f -name "*rsp" -print | zip -@ "$result_file" + +echo "Result file is: cavs/$result_file" >&2 diff --git a/tests/fipsdrv.c b/tests/fipsdrv.c index f9d9c45..9ad23a1 100644 --- a/tests/fipsdrv.c +++ b/tests/fipsdrv.c @@ -856,6 +856,9 @@ print_mpi_line (gcry_mpi_t a, int no_lz) die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err)); p = buf; + while (*p) + *p++ = tolower(*p); + p = buf; if (no_lz && p[0] == '0' && p[1] == '0' && p[2]) p += 2; @@ -1729,14 +1732,14 @@ run_rsa_verify (const void *data, size_t datalen, int hashalgo, int pkcs1, /* Generate a DSA key of size KEYSIZE and return the complete S-expression. */ static gcry_sexp_t -dsa_gen (int keysize) +dsa_gen (int keysize, int qsize) { gpg_error_t err; gcry_sexp_t keyspec, key; err = gcry_sexp_build (&keyspec, NULL, - "(genkey (dsa (nbits %d)(use-fips186-2)))", - keysize); + "(genkey (dsa (nbits %d)(qbits %d)(use-fips186)))", + keysize, qsize); if (err) die ("gcry_sexp_build failed for DSA key generation: %s\n", gpg_strerror (err)); @@ -1754,7 +1757,7 @@ dsa_gen (int keysize) /* Generate a DSA key of size KEYSIZE and return the complete S-expression. */ static gcry_sexp_t -dsa_gen_with_seed (int keysize, const void *seed, size_t seedlen) +dsa_gen_with_seed (int keysize, int qsize, const void *seed, size_t seedlen) { gpg_error_t err; gcry_sexp_t keyspec, key; @@ -1763,10 +1766,11 @@ dsa_gen_with_seed (int keysize, const void *seed, size_t seedlen) "(genkey" " (dsa" " (nbits %d)" - " (use-fips186-2)" + " (qbits %d)" + " (use-fips186)" " (derive-parms" " (seed %b))))", - keysize, (int)seedlen, seed); + keysize, qsize, (int)seedlen, seed); if (err) die ("gcry_sexp_build failed for DSA key generation: %s\n", gpg_strerror (err)); @@ -1774,6 +1778,37 @@ dsa_gen_with_seed (int keysize, const void *seed, size_t seedlen) err = gcry_pk_genkey (&key, keyspec); if (err) die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err)); + + gcry_sexp_release (keyspec); + + return key; +} + +/* Generate a DSA key with specified domain parameters and return the complete + S-expression. */ +static gcry_sexp_t +dsa_gen_key (const char *domain) +{ + gpg_error_t err; + gcry_sexp_t keyspec, key, domspec; + + err = gcry_sexp_new (&domspec, domain, strlen(domain), 0); + if (err) + die ("gcry_sexp_build failed for domain spec: %s\n", + gpg_strerror (err)); + + err = gcry_sexp_build (&keyspec, NULL, + "(genkey" + " (dsa" + " (use-fips186)" + " %S))", + domspec); + if (err) + die ("gcry_sexp_build failed for DSA key generation: %s\n", + gpg_strerror (err)); + err = gcry_pk_genkey (&key, keyspec); + if (err) + die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err)); gcry_sexp_release (keyspec); @@ -1813,7 +1848,7 @@ ecdsa_gen_key (const char *curve) with one parameter per line in hex format using this order: p, q, g, seed, counter, h. */ static void -print_dsa_domain_parameters (gcry_sexp_t key) +print_dsa_domain_parameters (gcry_sexp_t key, int print_misc) { gcry_sexp_t l1, l2; gcry_mpi_t mpi; @@ -1849,6 +1884,9 @@ print_dsa_domain_parameters (gcry_sexp_t key) } gcry_sexp_release (l1); + if (!print_misc) + return; + /* Extract the seed values. */ l1 = gcry_sexp_find_token (key, "misc-key-info", 0); if (!l1) @@ -1940,38 +1978,106 @@ print_ecdsa_dq (gcry_sexp_t key) } -/* Generate DSA domain parameters for a modulus size of KEYSIZE. The +/* Print just the XY private key parameters. KEY + is the complete key as returned by dsa_gen. We print to stdout + with one parameter per line in hex format using this order: x, y. */ +static void +print_dsa_xy (gcry_sexp_t key) +{ + gcry_sexp_t l1, l2; + gcry_mpi_t mpi; + int idx; + + l1 = gcry_sexp_find_token (key, "private-key", 0); + if (!l1) + die ("private key not found in genkey result\n"); + + l2 = gcry_sexp_find_token (l1, "dsa", 0); + if (!l2) + die ("returned private key not formed as expected\n"); + gcry_sexp_release (l1); + l1 = l2; + + /* Extract the parameters from the S-expression and print them to stdout. */ + for (idx=0; "xy"[idx]; idx++) + { + l2 = gcry_sexp_find_token (l1, "xy"+idx, 1); + if (!l2) + die ("no %c parameter in returned public key\n", "xy"[idx]); + mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + if (!mpi) + die ("no value for %c parameter in returned private key\n","xy"[idx]); + gcry_sexp_release (l2); + if (standalone_mode) + printf ("%c = ", "XY"[idx]); + print_mpi_line (mpi, 1); + gcry_mpi_release (mpi); + } + + gcry_sexp_release (l1); +} + + +/* Generate DSA pq domain parameters for a modulus size of KEYSIZE. The result is printed to stdout with one parameter per line in hex - format and in this order: p, q, g, seed, counter, h. If SEED is + format and in this order: p, q, seed, counter. If SEED is not NULL this seed value will be used for the generation. */ static void -run_dsa_pqg_gen (int keysize, const void *seed, size_t seedlen) +run_dsa_pqg_gen (int keysize, int qsize, const void *seed, size_t seedlen) { gcry_sexp_t key; if (seed) - key = dsa_gen_with_seed (keysize, seed, seedlen); + key = dsa_gen_with_seed (keysize, qsize, seed, seedlen); else - key = dsa_gen (keysize); - print_dsa_domain_parameters (key); + key = dsa_gen (keysize, qsize); + print_dsa_domain_parameters (key, 1); + gcry_sexp_release (key); +} + + +/* Generate DSA domain parameters for a modulus size of KEYSIZE. The + result is printed to stdout with one parameter per line in hex + format and in this order: p, q, g, seed, counter, h. If SEED is + not NULL this seed value will be used for the generation. */ +static void +run_dsa_g_gen (int keysize, int qsize, const char *domain) +{ + gcry_sexp_t key; + + key = dsa_gen_key (domain); + print_dsa_domain_parameters (key, 0); + gcry_sexp_release (key); +} + +/* Generate a DSA key with specified domain parameters + and print the XY values. */ +static void +run_dsa_gen_key (const char *domain) +{ + gcry_sexp_t key; + + key = dsa_gen_key (domain); + print_dsa_xy (key); + gcry_sexp_release (key); } /* Generate a DSA key of size of KEYSIZE and write the private key to FILENAME. Also write the parameters to stdout in the same way as - run_dsa_pqg_gen. */ + run_dsa_g_gen. */ static void -run_dsa_gen (int keysize, const char *filename) +run_dsa_gen (int keysize, int qsize, const char *filename) { gcry_sexp_t key, private_key; FILE *fp; - key = dsa_gen (keysize); + key = dsa_gen (keysize, qsize); private_key = gcry_sexp_find_token (key, "private-key", 0); if (!private_key) die ("private key not found in genkey result\n"); - print_dsa_domain_parameters (key); + print_dsa_domain_parameters (key, 1); fp = fopen (filename, "wb"); if (!fp) @@ -1984,6 +2090,53 @@ run_dsa_gen (int keysize, const char *filename) } +static int +dsa_hash_from_key(gcry_sexp_t s_key) +{ + gcry_sexp_t l1, l2; + gcry_mpi_t q; + unsigned int qbits; + + l1 = gcry_sexp_find_token (s_key, "public-key", 0); + if (!l1) + { + l1 = gcry_sexp_find_token (s_key, "private-key", 0); + if (!l1) + die ("neither private nor public key found in the loaded key\n"); + } + + l2 = gcry_sexp_find_token (l1, "dsa", 0); + if (!l2) + die ("public key not formed as expected - no dsa\n"); + gcry_sexp_release (l1); + l1 = l2; + + l2 = gcry_sexp_find_token (l1, "q", 0); + if (!l2) + die ("public key not formed as expected - no q\n"); + gcry_sexp_release (l1); + l1 = l2; + + q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + if (!q) + die ("public key not formed as expected - no mpi in q\n"); + qbits = gcry_mpi_get_nbits(q); + gcry_sexp_release(l1); + gcry_mpi_release(q); + switch(qbits) + { + case 160: + return GCRY_MD_SHA1; + case 224: + return GCRY_MD_SHA224; + case 256: + return GCRY_MD_SHA256; + default: + die("bad number bits (%d) of q in key\n", qbits); + } + return GCRY_MD_NONE; +} + /* Sign DATA of length DATALEN using the key taken from the S-expression encoded KEYFILE. */ @@ -1993,11 +2146,16 @@ run_dsa_sign (const void *data, size_t datalen, const char *keyfile) { gpg_error_t err; gcry_sexp_t s_data, s_key, s_sig, s_tmp, s_tmp2; - char hash[20]; + char hash[128]; gcry_mpi_t tmpmpi; + int algo; + + s_key = read_sexp_from_file (keyfile); + algo = dsa_hash_from_key(s_key); - gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen); - err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL); + gcry_md_hash_buffer (algo, hash, data, datalen); + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, + gcry_md_get_algo_dlen(algo), NULL); if (!err) { err = gcry_sexp_build (&s_data, NULL, @@ -2008,8 +2166,6 @@ run_dsa_sign (const void *data, size_t datalen, const char *keyfile) die ("gcry_sexp_build failed for DSA data input: %s\n", gpg_strerror (err)); - s_key = read_sexp_from_file (keyfile); - err = gcry_pk_sign (&s_sig, s_data, s_key); if (err) { @@ -2085,13 +2241,18 @@ run_dsa_verify (const void *data, size_t datalen, { gpg_error_t err; gcry_sexp_t s_data, s_key, s_sig; - char hash[20]; + char hash[128]; gcry_mpi_t tmpmpi; + int algo; - gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen); + s_key = read_sexp_from_file (keyfile); + algo = dsa_hash_from_key(s_key); + + gcry_md_hash_buffer (algo, hash, data, datalen); /* Note that we can't simply use %b with HASH to build the S-expression, because that might yield a negative value. */ - err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL); + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, + gcry_md_get_algo_dlen(algo), NULL); if (!err) { err = gcry_sexp_build (&s_data, NULL, @@ -2102,7 +2263,6 @@ run_dsa_verify (const void *data, size_t datalen, die ("gcry_sexp_build failed for DSA data input: %s\n", gpg_strerror (err)); - s_key = read_sexp_from_file (keyfile); s_sig = read_sexp_from_file (sigfile); err = gcry_pk_verify (s_sig, s_data, s_key); @@ -2268,7 +2428,7 @@ usage (int show_help) "MODE:\n" " encrypt, decrypt, digest, random, hmac-sha,\n" " rsa-{derive,gen,sign,verify},\n" - " dsa-{pqg-gen,gen,sign,verify}, ecdsa-{gen-key,sign,verify}\n" + " dsa-{pq-gen,g-gen,gen,sign,verify}, ecdsa-{gen-key,sign,verify}\n" "OPTIONS:\n" " --verbose Print additional information\n" " --binary Input and output is in binary form\n" @@ -2279,6 +2439,7 @@ usage (int show_help) " --algo NAME Use algorithm NAME\n" " --curve NAME Select ECC curve spec NAME\n" " --keysize N Use a keysize of N bits\n" + " --qize N Use a DSA q parameter size of N bits\n" " --signature NAME Take signature from file NAME\n" " --chunk N Read in chunks of N bytes (implies --binary)\n" " --pkcs1 Use PKCS#1 encoding\n" @@ -2308,6 +2469,7 @@ main (int argc, char **argv) const char *dt_string = NULL; const char *algo_string = NULL; const char *keysize_string = NULL; + const char *qsize_string = NULL; const char *signature_string = NULL; FILE *input; void *data; @@ -2401,6 +2563,14 @@ main (int argc, char **argv) keysize_string = *argv; argc--; argv++; } + else if (!strcmp (*argv, "--qsize")) + { + argc--; argv++; + if (!argc) + usage (0); + qsize_string = *argv; + argc--; argv++; + } else if (!strcmp (*argv, "--signature")) { argc--; argv++; @@ -2756,23 +2926,49 @@ main (int argc, char **argv) } else if (!strcmp (mode_string, "dsa-pqg-gen")) { - int keysize; + int keysize, qsize; + + keysize = keysize_string? atoi (keysize_string) : 0; + if (keysize < 1024 || keysize > 3072) + die ("invalid keysize specified; needs to be 1024 .. 3072\n"); + qsize = qsize_string? atoi (qsize_string) : 0; + if (qsize < 160 || qsize > 256) + die ("invalid qsize specified; needs to be 160 .. 256\n"); + run_dsa_pqg_gen (keysize, qsize, datalen? data:NULL, datalen); + } + else if (!strcmp (mode_string, "dsa-g-gen")) + { + int keysize, qsize; keysize = keysize_string? atoi (keysize_string) : 0; if (keysize < 1024 || keysize > 3072) die ("invalid keysize specified; needs to be 1024 .. 3072\n"); - run_dsa_pqg_gen (keysize, datalen? data:NULL, datalen); + qsize = qsize_string? atoi (qsize_string) : 0; + if (qsize < 160 || qsize > 256) + die ("invalid qsize specified; needs to be 160 .. 256\n"); + if (!key_string) + die ("option --key containing pq domain parameters is required in this mode\n"); + run_dsa_g_gen (keysize, qsize, key_string); + } + else if (!strcmp (mode_string, "dsa-gen-key")) + { + if (!key_string) + die ("option --key containing pqg domain parameters is required in this mode\n"); + run_dsa_gen_key (key_string); } else if (!strcmp (mode_string, "dsa-gen")) { - int keysize; + int keysize, qsize; keysize = keysize_string? atoi (keysize_string) : 0; if (keysize < 1024 || keysize > 3072) die ("invalid keysize specified; needs to be 1024 .. 3072\n"); + qsize = qsize_string? atoi (qsize_string) : 0; + if (qsize < 160 || qsize > 256) + die ("invalid qsize specified; needs to be 160 .. 256\n"); if (!key_string) die ("option --key is required in this mode\n"); - run_dsa_gen (keysize, key_string); + run_dsa_gen (keysize, qsize, key_string); } else if (!strcmp (mode_string, "dsa-sign")) { diff --git a/tests/fipsdrv.c.cavs b/tests/fipsdrv.c.cavs new file mode 100644 index 0000000..f9d9c45 --- /dev/null +++ b/tests/fipsdrv.c.cavs @@ -0,0 +1,2865 @@ +/* fipsdrv.c - A driver to help with FIPS CAVS tests. + Copyright (C) 2008 Free Software Foundation, Inc. + + This file is part of Libgcrypt. + + Libgcrypt is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + Libgcrypt is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef HAVE_W32_SYSTEM +# include /* We need setmode(). */ +#else +# include +#endif +#include +#include + +#ifndef _GCRYPT_IN_LIBGCRYPT +# include +# define PACKAGE_BUGREPORT "devnull@example.org" +# define PACKAGE_VERSION "[build on " __DATE__ " " __TIME__ "]" +#endif +#include "../src/gcrypt-testapi.h" + +#define PGM "fipsdrv" +#include "t-common.h" + + +/* Binary input flag. */ +static int binary_input; + +/* Binary output flag. */ +static int binary_output; + +/* Base64 output flag. */ +static int base64_output; + +/* We need to know whether we are in loop_mode. */ +static int loop_mode; + +/* If true some functions are modified to print the output in the CAVS + response file format. */ +static int standalone_mode; + + +/* ASN.1 classes. */ +enum +{ + UNIVERSAL = 0, + APPLICATION = 1, + ASNCONTEXT = 2, + PRIVATE = 3 +}; + + +/* ASN.1 tags. */ +enum +{ + TAG_NONE = 0, + TAG_BOOLEAN = 1, + TAG_INTEGER = 2, + TAG_BIT_STRING = 3, + TAG_OCTET_STRING = 4, + TAG_NULL = 5, + TAG_OBJECT_ID = 6, + TAG_OBJECT_DESCRIPTOR = 7, + TAG_EXTERNAL = 8, + TAG_REAL = 9, + TAG_ENUMERATED = 10, + TAG_EMBEDDED_PDV = 11, + TAG_UTF8_STRING = 12, + TAG_REALTIVE_OID = 13, + TAG_SEQUENCE = 16, + TAG_SET = 17, + TAG_NUMERIC_STRING = 18, + TAG_PRINTABLE_STRING = 19, + TAG_TELETEX_STRING = 20, + TAG_VIDEOTEX_STRING = 21, + TAG_IA5_STRING = 22, + TAG_UTC_TIME = 23, + TAG_GENERALIZED_TIME = 24, + TAG_GRAPHIC_STRING = 25, + TAG_VISIBLE_STRING = 26, + TAG_GENERAL_STRING = 27, + TAG_UNIVERSAL_STRING = 28, + TAG_CHARACTER_STRING = 29, + TAG_BMP_STRING = 30 +}; + +/* ASN.1 Parser object. */ +struct tag_info +{ + int class; /* Object class. */ + unsigned long tag; /* The tag of the object. */ + unsigned long length; /* Length of the values. */ + int nhdr; /* Length of the header (TL). */ + unsigned int ndef:1; /* The object has an indefinite length. */ + unsigned int cons:1; /* This is a constructed object. */ +}; + + +static void +showhex (const char *prefix, const void *buffer, size_t length) +{ + const unsigned char *p = buffer; + + if (prefix) + fprintf (stderr, PGM ": %s: ", prefix); + while (length-- ) + fprintf (stderr, "%02X", *p++); + if (prefix) + putc ('\n', stderr); +} + +/* static void */ +/* show_sexp (const char *prefix, gcry_sexp_t a) */ +/* { */ +/* char *buf; */ +/* size_t size; */ + +/* if (prefix) */ +/* fputs (prefix, stderr); */ +/* size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); */ +/* buf = gcry_xmalloc (size); */ + +/* gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); */ +/* fprintf (stderr, "%.*s", (int)size, buf); */ +/* gcry_free (buf); */ +/* } */ + + +/* Convert STRING consisting of hex characters into its binary + representation and store that at BUFFER. BUFFER needs to be of + LENGTH bytes. The function checks that the STRING will convert + exactly to LENGTH bytes. The string is delimited by either end of + string or a white space character. The function returns -1 on + error or the length of the parsed string. */ +static int +hex2bin (const char *string, void *buffer, size_t length) +{ + int i; + const char *s = string; + + for (i=0; i < length; ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + return -1; /* Invalid hex digits. */ + ((unsigned char*)buffer)[i++] = xtoi_2 (s); + s += 2; + } + if (*s && (!my_isascii (*s) || !isspace (*s)) ) + return -1; /* Not followed by Nul or white space. */ + if (i != length) + return -1; /* Not of expected length. */ + if (*s) + s++; /* Skip the delimiter. */ + return s - string; +} + + +/* Convert STRING consisting of hex characters into its binary + representation and return it as an allocated buffer. The valid + length of the buffer is returned at R_LENGTH. The string is + delimited by end of string. The function returns NULL on + error. */ +static void * +hex2buffer (const char *string, size_t *r_length) +{ + const char *s; + unsigned char *buffer; + size_t length; + + buffer = gcry_xmalloc (strlen(string)/2+1); + length = 0; + for (s=string; *s; s +=2 ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + return NULL; /* Invalid hex digits. */ + ((unsigned char*)buffer)[length++] = xtoi_2 (s); + } + *r_length = length; + return buffer; +} + + +static char * +read_textline (FILE *fp) +{ + char line[256]; + char *p; + int any = 0; + + /* Read line but skip over initial empty lines. */ + do + { + do + { + if (!fgets (line, sizeof line, fp)) + { + if (feof (fp)) + return NULL; + die ("error reading input line: %s\n", strerror (errno)); + } + p = strchr (line, '\n'); + if (p) + *p = 0; + p = line + (*line? (strlen (line)-1):0); + for ( ;p > line; p--) + if (my_isascii (*p) && isspace (*p)) + *p = 0; + } + while (!any && !*line); + any = 1; + } + while (*line == '#'); /* Always skip comment lines. */ + if (verbose > 1) + fprintf (stderr, PGM ": received line: %s\n", line); + return gcry_xstrdup (line); +} + +static char * +read_hexline (FILE *fp, size_t *retlen) +{ + char *line, *p; + + line = read_textline (fp); + if (!line) + return NULL; + p = hex2buffer (line, retlen); + if (!p) + die ("error decoding hex string on input\n"); + gcry_free (line); + return p; +} + +static void +skip_to_empty_line (FILE *fp) +{ + char line[256]; + char *p; + + do + { + if (!fgets (line, sizeof line, fp)) + { + if (feof (fp)) + return; + die ("error reading input line: %s\n", strerror (errno)); + } + p = strchr (line, '\n'); + if (p) + *p =0; + } + while (*line); +} + + + +/* Read a file from stream FP into a newly allocated buffer and return + that buffer. The valid length of the buffer is stored at R_LENGTH. + Returns NULL on failure. If decode is set, the file is assumed to + be hex encoded and the decoded content is returned. */ +static void * +read_file (FILE *fp, int decode, size_t *r_length) +{ + char *buffer; + size_t buflen; + size_t nread, bufsize = 0; + + *r_length = 0; +#define NCHUNK 8192 +#ifdef HAVE_DOSISH_SYSTEM + setmode (fileno(fp), O_BINARY); +#endif + buffer = NULL; + buflen = 0; + do + { + bufsize += NCHUNK; + if (!buffer) + buffer = gcry_xmalloc (bufsize); + else + buffer = gcry_xrealloc (buffer, bufsize); + + nread = fread (buffer + buflen, 1, NCHUNK, fp); + if (nread < NCHUNK && ferror (fp)) + { + gcry_free (buffer); + return NULL; + } + buflen += nread; + } + while (nread == NCHUNK); +#undef NCHUNK + if (decode) + { + const char *s; + char *p; + + for (s=buffer,p=buffer,nread=0; nread+1 < buflen; s += 2, nread +=2 ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + { + gcry_free (buffer); + return NULL; /* Invalid hex digits. */ + } + *(unsigned char*)p++ = xtoi_2 (s); + } + if (nread != buflen) + { + gcry_free (buffer); + return NULL; /* Odd number of hex digits. */ + } + buflen = p - buffer; + } + + *r_length = buflen; + return buffer; +} + +/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Returns + the new length of the buffer. Dies on error. */ +static size_t +base64_decode (char *buffer, size_t length) +{ + static unsigned char const asctobin[128] = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + int idx = 0; + unsigned char val = 0; + int c = 0; + char *d, *s; + int lfseen = 1; + + /* Find BEGIN line. */ + for (s=buffer; length; length--, s++) + { + if (lfseen && *s == '-' && length > 11 && !memcmp (s, "-----BEGIN ", 11)) + { + for (; length && *s != '\n'; length--, s++) + ; + break; + } + lfseen = (*s == '\n'); + } + + /* Decode until pad character or END line. */ + for (d=buffer; length; length--, s++) + { + if (lfseen && *s == '-' && length > 9 && !memcmp (s, "-----END ", 9)) + break; + if ((lfseen = (*s == '\n')) || *s == ' ' || *s == '\r' || *s == '\t') + continue; + if (*s == '=') + { + /* Pad character: stop */ + if (idx == 1) + *d++ = val; + break; + } + + if ( (*s & 0x80) || (c = asctobin[*(unsigned char *)s]) == 0xff) + die ("invalid base64 character %02X at pos %d detected\n", + *(unsigned char*)s, (int)(s-buffer)); + + switch (idx) + { + case 0: + val = c << 2; + break; + case 1: + val |= (c>>4)&3; + *d++ = val; + val = (c<<4)&0xf0; + break; + case 2: + val |= (c>>2)&15; + *d++ = val; + val = (c<<6)&0xc0; + break; + case 3: + val |= c&0x3f; + *d++ = val; + break; + } + idx = (idx+1) % 4; + } + + return d - buffer; +} + + +/* Parse the buffer at the address BUFFER which consists of the number + of octets as stored at BUFLEN. Return the tag and the length part + from the TLV triplet. Update BUFFER and BUFLEN on success. Checks + that the encoded length does not exhaust the length of the provided + buffer. */ +static int +parse_tag (unsigned char const **buffer, size_t *buflen, struct tag_info *ti) +{ + int c; + unsigned long tag; + const unsigned char *buf = *buffer; + size_t length = *buflen; + + ti->length = 0; + ti->ndef = 0; + ti->nhdr = 0; + + /* Get the tag */ + if (!length) + return -1; /* Premature EOF. */ + c = *buf++; length--; + ti->nhdr++; + + ti->class = (c & 0xc0) >> 6; + ti->cons = !!(c & 0x20); + tag = (c & 0x1f); + + if (tag == 0x1f) + { + tag = 0; + do + { + tag <<= 7; + if (!length) + return -1; /* Premature EOF. */ + c = *buf++; length--; + ti->nhdr++; + tag |= (c & 0x7f); + } + while ( (c & 0x80) ); + } + ti->tag = tag; + + /* Get the length */ + if (!length) + return -1; /* Premature EOF. */ + c = *buf++; length--; + ti->nhdr++; + + if ( !(c & 0x80) ) + ti->length = c; + else if (c == 0x80) + ti->ndef = 1; + else if (c == 0xff) + return -1; /* Forbidden length value. */ + else + { + unsigned long len = 0; + int count = c & 0x7f; + + for (; count; count--) + { + len <<= 8; + if (!length) + return -1; /* Premature EOF. */ + c = *buf++; length--; + ti->nhdr++; + len |= (c & 0xff); + } + ti->length = len; + } + + if (ti->class == UNIVERSAL && !ti->tag) + ti->length = 0; + + if (ti->length > length) + return -1; /* Data larger than buffer. */ + + *buffer = buf; + *buflen = length; + return 0; +} + + +/* Read the file FNAME assuming it is a PEM encoded private key file + and return an S-expression. With SHOW set, the key parameters are + printed. */ +static gcry_sexp_t +read_private_key_file (const char *fname, int show) +{ + gcry_error_t err; + FILE *fp; + char *buffer; + size_t buflen; + const unsigned char *der; + size_t derlen; + struct tag_info ti; + gcry_mpi_t keyparms[8]; + int n_keyparms = 8; + int idx; + gcry_sexp_t s_key; + + fp = fopen (fname, binary_input?"rb":"r"); + if (!fp) + die ("can't open `%s': %s\n", fname, strerror (errno)); + buffer = read_file (fp, 0, &buflen); + if (!buffer) + die ("error reading `%s'\n", fname); + fclose (fp); + + buflen = base64_decode (buffer, buflen); + + /* Parse the ASN.1 structure. */ + der = (const unsigned char*)buffer; + derlen = buflen; + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) + goto bad_asn1; + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef) + goto bad_asn1; + if (ti.length != 1 || *der) + goto bad_asn1; /* The value of the first integer is no 0. */ + der += ti.length; derlen -= ti.length; + + for (idx=0; idx < n_keyparms; idx++) + { + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef) + goto bad_asn1; + if (show) + { + char prefix[2]; + + prefix[0] = idx < 8? "nedpq12u"[idx] : '?'; + prefix[1] = 0; + showhex (prefix, der, ti.length); + } + err = gcry_mpi_scan (keyparms+idx, GCRYMPI_FMT_USG, der, ti.length,NULL); + if (err) + die ("error scanning RSA parameter %d: %s\n", idx, gpg_strerror (err)); + der += ti.length; derlen -= ti.length; + } + if (idx != n_keyparms) + die ("not enough RSA key parameters\n"); + + gcry_free (buffer); + + /* Convert from OpenSSL parameter ordering to the OpenPGP order. */ + /* First check that p < q; if not swap p and q and recompute u. */ + if (gcry_mpi_cmp (keyparms[3], keyparms[4]) > 0) + { + gcry_mpi_swap (keyparms[3], keyparms[4]); + gcry_mpi_invm (keyparms[7], keyparms[3], keyparms[4]); + } + + /* Build the S-expression. */ + err = gcry_sexp_build (&s_key, NULL, + "(private-key(rsa(n%m)(e%m)" + /**/ "(d%m)(p%m)(q%m)(u%m)))", + keyparms[0], keyparms[1], keyparms[2], + keyparms[3], keyparms[4], keyparms[7] ); + if (err) + die ("error building S-expression: %s\n", gpg_strerror (err)); + + for (idx=0; idx < n_keyparms; idx++) + gcry_mpi_release (keyparms[idx]); + + return s_key; + + bad_asn1: + die ("invalid ASN.1 structure in `%s'\n", fname); + return NULL; /*NOTREACHED*/ +} + + +/* Read the file FNAME assuming it is a PEM encoded public key file + and return an S-expression. With SHOW set, the key parameters are + printed. */ +static gcry_sexp_t +read_public_key_file (const char *fname, int show) +{ + gcry_error_t err; + FILE *fp; + char *buffer; + size_t buflen; + const unsigned char *der; + size_t derlen; + struct tag_info ti; + gcry_mpi_t keyparms[2]; + int n_keyparms = 2; + int idx; + gcry_sexp_t s_key; + + fp = fopen (fname, binary_input?"rb":"r"); + if (!fp) + die ("can't open `%s': %s\n", fname, strerror (errno)); + buffer = read_file (fp, 0, &buflen); + if (!buffer) + die ("error reading `%s'\n", fname); + fclose (fp); + + buflen = base64_decode (buffer, buflen); + + /* Parse the ASN.1 structure. */ + der = (const unsigned char*)buffer; + derlen = buflen; + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) + goto bad_asn1; + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) + goto bad_asn1; + /* We skip the description of the key parameters and assume it is RSA. */ + der += ti.length; derlen -= ti.length; + + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_BIT_STRING || ti.class || ti.cons || ti.ndef) + goto bad_asn1; + if (ti.length < 1 || *der) + goto bad_asn1; /* The number of unused bits needs to be 0. */ + der += 1; derlen -= 1; + + /* Parse the BIT string. */ + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) + goto bad_asn1; + + for (idx=0; idx < n_keyparms; idx++) + { + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef) + goto bad_asn1; + if (show) + { + char prefix[2]; + + prefix[0] = idx < 2? "ne"[idx] : '?'; + prefix[1] = 0; + showhex (prefix, der, ti.length); + } + err = gcry_mpi_scan (keyparms+idx, GCRYMPI_FMT_USG, der, ti.length,NULL); + if (err) + die ("error scanning RSA parameter %d: %s\n", idx, gpg_strerror (err)); + der += ti.length; derlen -= ti.length; + } + if (idx != n_keyparms) + die ("not enough RSA key parameters\n"); + + gcry_free (buffer); + + /* Build the S-expression. */ + err = gcry_sexp_build (&s_key, NULL, + "(public-key(rsa(n%m)(e%m)))", + keyparms[0], keyparms[1] ); + if (err) + die ("error building S-expression: %s\n", gpg_strerror (err)); + + for (idx=0; idx < n_keyparms; idx++) + gcry_mpi_release (keyparms[idx]); + + return s_key; + + bad_asn1: + die ("invalid ASN.1 structure in `%s'\n", fname); + return NULL; /*NOTREACHED*/ +} + + + +/* Read the file FNAME assuming it is a binary signature result and + return an an S-expression suitable for gcry_pk_verify. */ +static gcry_sexp_t +read_sig_file (const char *fname) +{ + gcry_error_t err; + FILE *fp; + char *buffer; + size_t buflen; + gcry_mpi_t tmpmpi; + gcry_sexp_t s_sig; + + fp = fopen (fname, "rb"); + if (!fp) + die ("can't open `%s': %s\n", fname, strerror (errno)); + buffer = read_file (fp, 0, &buflen); + if (!buffer) + die ("error reading `%s'\n", fname); + fclose (fp); + + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, buffer, buflen, NULL); + if (!err) + err = gcry_sexp_build (&s_sig, NULL, + "(sig-val(rsa(s %m)))", tmpmpi); + if (err) + die ("error building S-expression: %s\n", gpg_strerror (err)); + gcry_mpi_release (tmpmpi); + gcry_free (buffer); + + return s_sig; +} + + +/* Read an S-expression from FNAME. */ +static gcry_sexp_t +read_sexp_from_file (const char *fname) +{ + gcry_error_t err; + FILE *fp; + char *buffer; + size_t buflen; + gcry_sexp_t sexp; + + fp = fopen (fname, "rb"); + if (!fp) + die ("can't open `%s': %s\n", fname, strerror (errno)); + buffer = read_file (fp, 0, &buflen); + if (!buffer) + die ("error reading `%s'\n", fname); + fclose (fp); + if (!buflen) + die ("error: file `%s' is empty\n", fname); + + err = gcry_sexp_create (&sexp, buffer, buflen, 1, gcry_free); + if (err) + die ("error parsing `%s': %s\n", fname, gpg_strerror (err)); + + return sexp; +} + + +static void +print_buffer (const void *buffer, size_t length) +{ + int writerr = 0; + + if (base64_output) + { + static const unsigned char bintoasc[64+1] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + const unsigned char *p; + unsigned char inbuf[4]; + char outbuf[4]; + int idx, quads; + + idx = quads = 0; + for (p = buffer; length; p++, length--) + { + inbuf[idx++] = *p; + if (idx > 2) + { + outbuf[0] = bintoasc[(*inbuf>>2)&077]; + outbuf[1] = bintoasc[(((*inbuf<<4)&060) + |((inbuf[1] >> 4)&017))&077]; + outbuf[2] = bintoasc[(((inbuf[1]<<2)&074) + |((inbuf[2]>>6)&03))&077]; + outbuf[3] = bintoasc[inbuf[2]&077]; + if (fwrite (outbuf, 4, 1, stdout) != 1) + writerr = 1; + idx = 0; + if (++quads >= (64/4)) + { + if (fwrite ("\n", 1, 1, stdout) != 1) + writerr = 1; + quads = 0; + } + } + } + if (idx) + { + outbuf[0] = bintoasc[(*inbuf>>2)&077]; + if (idx == 1) + { + outbuf[1] = bintoasc[((*inbuf<<4)&060)&077]; + outbuf[2] = outbuf[3] = '='; + } + else + { + outbuf[1] = bintoasc[(((*inbuf<<4)&060) + |((inbuf[1]>>4)&017))&077]; + outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077]; + outbuf[3] = '='; + } + if (fwrite (outbuf, 4, 1, stdout) != 1) + writerr = 1; + quads++; + } + if (quads && fwrite ("\n", 1, 1, stdout) != 1) + writerr = 1; + } + else if (binary_output) + { + if (fwrite (buffer, length, 1, stdout) != 1) + writerr++; + } + else + { + const unsigned char *p = buffer; + + if (verbose > 1) + showhex ("sent line", buffer, length); + while (length-- && !ferror (stdout) ) + printf ("%02X", *p++); + if (ferror (stdout)) + writerr++; + } + if (!writerr && fflush (stdout) == EOF) + writerr++; + if (writerr) + { +#ifndef HAVE_W32_SYSTEM + if (loop_mode && errno == EPIPE) + loop_mode = 0; + else +#endif + die ("writing output failed: %s\n", strerror (errno)); + } +} + + +/* Print an MPI on a line. */ +static void +print_mpi_line (gcry_mpi_t a, int no_lz) +{ + unsigned char *buf, *p; + gcry_error_t err; + int writerr = 0; + + err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buf, NULL, a); + if (err) + die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err)); + + p = buf; + if (no_lz && p[0] == '0' && p[1] == '0' && p[2]) + p += 2; + + printf ("%s\n", p); + if (ferror (stdout)) + writerr++; + if (!writerr && fflush (stdout) == EOF) + writerr++; + if (writerr) + die ("writing output failed: %s\n", strerror (errno)); + gcry_free (buf); +} + + +/* Print some data on hex format on a line. */ +static void +print_data_line (const void *data, size_t datalen) +{ + const unsigned char *p = data; + int writerr = 0; + + while (data && datalen-- && !ferror (stdout) ) + printf ("%02X", *p++); + putchar ('\n'); + if (ferror (stdout)) + writerr++; + if (!writerr && fflush (stdout) == EOF) + writerr++; + if (writerr) + die ("writing output failed: %s\n", strerror (errno)); +} + +/* Print the S-expression A to the stream FP. */ +static void +print_sexp (gcry_sexp_t a, FILE *fp) +{ + char *buf; + size_t size; + + size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); + buf = gcry_xmalloc (size); + gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); + if (fwrite (buf, size, 1, fp) != 1) + die ("error writing to stream: %s\n", strerror (errno)); + gcry_free (buf); +} + + + + +static gcry_error_t +init_external_rng_test (void **r_context, + unsigned int flags, + const void *key, size_t keylen, + const void *seed, size_t seedlen, + const void *dt, size_t dtlen) +{ + return gcry_control (PRIV_CTL_INIT_EXTRNG_TEST, + r_context, flags, + key, keylen, + seed, seedlen, + dt, dtlen); +} + +static gcry_error_t +run_external_rng_test (void *context, void *buffer, size_t buflen) +{ + return gcry_control (PRIV_CTL_RUN_EXTRNG_TEST, context, buffer, buflen); +} + +static void +deinit_external_rng_test (void *context) +{ + xgcry_control (PRIV_CTL_DEINIT_EXTRNG_TEST, context); +} + + +/* Given an OpenSSL cipher name NAME, return the Libgcrypt algirithm + identified and store the libgcrypt mode at R_MODE. Returns 0 on + error. */ +static int +map_openssl_cipher_name (const char *name, int *r_mode) +{ + static struct { + const char *name; + int algo; + int mode; + } table[] = + { + { "bf-cbc", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC }, + { "bf", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC }, + { "bf-cfb", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB }, + { "bf-ecb", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB }, + { "bf-ofb", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB }, + + { "cast-cbc", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC }, + { "cast", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC }, + { "cast5-cbc", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC }, + { "cast5-cfb", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CFB }, + { "cast5-ecb", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_ECB }, + { "cast5-ofb", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_OFB }, + + { "des-cbc", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC }, + { "des", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC }, + { "des-cfb", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB }, + { "des-ofb", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_OFB }, + { "des-ecb", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB }, + + { "des-ede3-cbc", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC }, + { "des-ede3", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB }, + { "des3", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC }, + { "des-ede3-cfb", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CFB }, + { "des-ede3-ofb", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_OFB }, + + { "rc4", GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM }, + + { "aes-128-cbc", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC }, + { "aes-128", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC }, + { "aes-128-cfb", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB }, + { "aes-128-ecb", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB }, + { "aes-128-ofb", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OFB }, + + { "aes-192-cbc", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC }, + { "aes-192", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC }, + { "aes-192-cfb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB }, + { "aes-192-ecb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB }, + { "aes-192-ofb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB }, + + { "aes-256-cbc", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC }, + { "aes-256", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC }, + { "aes-256-cfb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB }, + { "aes-256-ecb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB }, + { "aes-256-ofb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB }, + + { NULL, 0 , 0 } + }; + int idx; + + for (idx=0; table[idx].name; idx++) + if (!strcmp (name, table[idx].name)) + { + *r_mode = table[idx].mode; + return table[idx].algo; + } + *r_mode = 0; + return 0; +} + + + +/* Run an encrypt or decryption operations. If DATA is NULL the + function reads its input in chunks of size DATALEN from fp and + processes it and writes it out until EOF. */ +static void +run_encrypt_decrypt (int encrypt_mode, + int cipher_algo, int cipher_mode, + const void *iv_buffer, size_t iv_buflen, + const void *key_buffer, size_t key_buflen, + const void *data, size_t datalen, FILE *fp) +{ + gpg_error_t err; + gcry_cipher_hd_t hd; + void *outbuf; + size_t outbuflen; + void *inbuf; + size_t inbuflen; + size_t blocklen; + + err = gcry_cipher_open (&hd, cipher_algo, cipher_mode, 0); + if (err) + die ("gcry_cipher_open failed for algo %d, mode %d: %s\n", + cipher_algo, cipher_mode, gpg_strerror (err)); + + blocklen = gcry_cipher_get_algo_blklen (cipher_algo); + assert (blocklen); + + gcry_cipher_ctl (hd, PRIV_CIPHERCTL_DISABLE_WEAK_KEY, NULL, 0); + + err = gcry_cipher_setkey (hd, key_buffer, key_buflen); + if (err) + die ("gcry_cipher_setkey failed with keylen %u: %s\n", + (unsigned int)key_buflen, gpg_strerror (err)); + + if (iv_buffer) + { + err = gcry_cipher_setiv (hd, iv_buffer, iv_buflen); + if (err) + die ("gcry_cipher_setiv failed with ivlen %u: %s\n", + (unsigned int)iv_buflen, gpg_strerror (err)); + } + + inbuf = data? NULL : gcry_xmalloc (datalen); + outbuflen = datalen; + outbuf = gcry_xmalloc (outbuflen < blocklen? blocklen:outbuflen); + + do + { + if (inbuf) + { + int nread = fread (inbuf, 1, datalen, fp); + if (nread < (int)datalen && ferror (fp)) + die ("error reading input\n"); + data = inbuf; + inbuflen = nread; + } + else + inbuflen = datalen; + + if (encrypt_mode) + err = gcry_cipher_encrypt (hd, outbuf, outbuflen, data, inbuflen); + else + err = gcry_cipher_decrypt (hd, outbuf, outbuflen, data, inbuflen); + if (err) + die ("gcry_cipher_%scrypt failed: %s\n", + encrypt_mode? "en":"de", gpg_strerror (err)); + + print_buffer (outbuf, outbuflen); + } + while (inbuf); + + gcry_cipher_close (hd); + gcry_free (outbuf); + gcry_free (inbuf); +} + + +static void +get_current_iv (gcry_cipher_hd_t hd, void *buffer, size_t buflen) +{ + unsigned char tmp[17]; + + if (gcry_cipher_ctl (hd, PRIV_CIPHERCTL_GET_INPUT_VECTOR, tmp, sizeof tmp)) + die ("error getting current input vector\n"); + if (buflen > *tmp) + die ("buffer too short to store the current input vector\n"); + memcpy (buffer, tmp+1, *tmp); +} + +/* Run the inner loop of the CAVS monte carlo test. */ +static void +run_cipher_mct_loop (int encrypt_mode, int cipher_algo, int cipher_mode, + const void *iv_buffer, size_t iv_buflen, + const void *key_buffer, size_t key_buflen, + const void *data, size_t datalen, int iterations) +{ + gpg_error_t err; + gcry_cipher_hd_t hd; + size_t blocklen; + int count; + char input[16]; + char output[16]; + char last_output[16]; + char last_last_output[16]; + char last_iv[16]; + + + err = gcry_cipher_open (&hd, cipher_algo, cipher_mode, 0); + if (err) + die ("gcry_cipher_open failed for algo %d, mode %d: %s\n", + cipher_algo, cipher_mode, gpg_strerror (err)); + + blocklen = gcry_cipher_get_algo_blklen (cipher_algo); + if (!blocklen || blocklen > sizeof output) + die ("invalid block length %d\n", (int)blocklen); + + + gcry_cipher_ctl (hd, PRIV_CIPHERCTL_DISABLE_WEAK_KEY, NULL, 0); + + err = gcry_cipher_setkey (hd, key_buffer, key_buflen); + if (err) + die ("gcry_cipher_setkey failed with keylen %u: %s\n", + (unsigned int)key_buflen, gpg_strerror (err)); + + if (iv_buffer) + { + err = gcry_cipher_setiv (hd, iv_buffer, iv_buflen); + if (err) + die ("gcry_cipher_setiv failed with ivlen %u: %s\n", + (unsigned int)iv_buflen, gpg_strerror (err)); + } + + if (datalen != blocklen) + die ("length of input (%u) does not match block length (%u)\n", + (unsigned int)datalen, (unsigned int)blocklen); + memcpy (input, data, datalen); + memset (output, 0, sizeof output); + for (count=0; count < iterations; count++) + { + memcpy (last_last_output, last_output, sizeof last_output); + memcpy (last_output, output, sizeof output); + + get_current_iv (hd, last_iv, blocklen); + + if (encrypt_mode) + err = gcry_cipher_encrypt (hd, output, blocklen, input, blocklen); + else + err = gcry_cipher_decrypt (hd, output, blocklen, input, blocklen); + if (err) + die ("gcry_cipher_%scrypt failed: %s\n", + encrypt_mode? "en":"de", gpg_strerror (err)); + + + if (encrypt_mode && (cipher_mode == GCRY_CIPHER_MODE_CFB + || cipher_mode == GCRY_CIPHER_MODE_CBC)) + memcpy (input, last_iv, blocklen); + else if (cipher_mode == GCRY_CIPHER_MODE_OFB) + memcpy (input, last_iv, blocklen); + else if (!encrypt_mode && cipher_mode == GCRY_CIPHER_MODE_CFB) + { + /* Reconstruct the output vector. */ + int i; + for (i=0; i < blocklen; i++) + input[i] ^= output[i]; + } + else + memcpy (input, output, blocklen); + } + + print_buffer (output, blocklen); + putchar ('\n'); + print_buffer (last_output, blocklen); + putchar ('\n'); + print_buffer (last_last_output, blocklen); + putchar ('\n'); + get_current_iv (hd, last_iv, blocklen); + print_buffer (last_iv, blocklen); /* Last output vector. */ + putchar ('\n'); + print_buffer (input, blocklen); /* Next input text. */ + putchar ('\n'); + if (verbose > 1) + showhex ("sent line", "", 0); + putchar ('\n'); + fflush (stdout); + + gcry_cipher_close (hd); +} + + + +/* Run a digest operation. */ +static void +run_digest (int digest_algo, const void *data, size_t datalen) +{ + gpg_error_t err; + gcry_md_hd_t hd; + const unsigned char *digest; + unsigned int digestlen; + + err = gcry_md_open (&hd, digest_algo, 0); + if (err) + die ("gcry_md_open failed for algo %d: %s\n", + digest_algo, gpg_strerror (err)); + + gcry_md_write (hd, data, datalen); + digest = gcry_md_read (hd, digest_algo); + digestlen = gcry_md_get_algo_dlen (digest_algo); + print_buffer (digest, digestlen); + gcry_md_close (hd); +} + + +/* Run a HMAC operation. */ +static void +run_hmac (int digest_algo, const void *key, size_t keylen, + const void *data, size_t datalen) +{ + gpg_error_t err; + gcry_md_hd_t hd; + const unsigned char *digest; + unsigned int digestlen; + + err = gcry_md_open (&hd, digest_algo, GCRY_MD_FLAG_HMAC); + if (err) + die ("gcry_md_open failed for HMAC algo %d: %s\n", + digest_algo, gpg_strerror (err)); + + gcry_md_setkey (hd, key, keylen); + if (err) + die ("gcry_md_setkey failed for HMAC algo %d: %s\n", + digest_algo, gpg_strerror (err)); + + gcry_md_write (hd, data, datalen); + digest = gcry_md_read (hd, digest_algo); + digestlen = gcry_md_get_algo_dlen (digest_algo); + print_buffer (digest, digestlen); + gcry_md_close (hd); +} + + + +/* Derive an RSA key using the S-expression in (DATA,DATALEN). This + S-expression is used directly as input to gcry_pk_genkey. The + result is printed to stdout with one parameter per line in hex + format and in this order: p, q, n, d. */ +static void +run_rsa_derive (const void *data, size_t datalen) +{ + gpg_error_t err; + gcry_sexp_t s_keyspec, s_key, s_top, l1; + gcry_mpi_t mpi; + const char *parmlist; + int idx; + + if (!datalen) + err = gpg_error (GPG_ERR_NO_DATA); + else + err = gcry_sexp_new (&s_keyspec, data, datalen, 1); + if (err) + die ("gcry_sexp_new failed for RSA key derive: %s\n", + gpg_strerror (err)); + + err = gcry_pk_genkey (&s_key, s_keyspec); + if (err) + die ("gcry_pk_genkey failed for RSA: %s\n", gpg_strerror (err)); + + gcry_sexp_release (s_keyspec); + + /* P and Q might have been swapped but we need to to return them in + the proper order. Build the parameter list accordingly. */ + parmlist = "pqnd"; + s_top = gcry_sexp_find_token (s_key, "misc-key-info", 0); + if (s_top) + { + l1 = gcry_sexp_find_token (s_top, "p-q-swapped", 0); + if (l1) + parmlist = "qpnd"; + gcry_sexp_release (l1); + gcry_sexp_release (s_top); + } + + /* Parse and print the parameters. */ + l1 = gcry_sexp_find_token (s_key, "private-key", 0); + s_top = gcry_sexp_find_token (l1, "rsa", 0); + gcry_sexp_release (l1); + if (!s_top) + die ("private-key part not found in result\n"); + + for (idx=0; parmlist[idx]; idx++) + { + l1 = gcry_sexp_find_token (s_top, parmlist+idx, 1); + mpi = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l1); + if (!mpi) + die ("parameter %c missing in private-key\n", parmlist[idx]); + print_mpi_line (mpi, 1); + gcry_mpi_release (mpi); + } + + gcry_sexp_release (s_top); + gcry_sexp_release (s_key); +} + + +/* Generate RSA key using the S-expression in (DATA,DATALEN). This + S-expression is used directly as input to gcry_pk_genkey. The + result is printed to stdout with one parameter per line in hex + format and in this order: e, p, q, n, d. */ +static void +run_rsa_keygen (const void *data, size_t datalen, int test) +{ + gpg_error_t err; + gcry_sexp_t s_keyspec, s_key, s_top, l1; + gcry_mpi_t mpi; + const char *parmlist; + int idx; + + if (!datalen) + err = gpg_error (GPG_ERR_NO_DATA); + else + err = gcry_sexp_new (&s_keyspec, data, datalen, 1); + if (err) + die ("gcry_sexp_new failed for RSA key generation: %s\n", + gpg_strerror (err)); + + err = gcry_pk_genkey (&s_key, s_keyspec); + + gcry_sexp_release (s_keyspec); + + if (test) { + if (err) + printf("F\n"); + else { + gcry_sexp_release (s_key); + printf("P\n"); + } + return; + } + + if (err) + die ("gcry_pk_genkey failed for RSA: %s\n", gpg_strerror (err)); + + parmlist = "epqnd"; + + /* Parse and print the parameters. */ + l1 = gcry_sexp_find_token (s_key, "private-key", 0); + s_top = gcry_sexp_find_token (l1, "rsa", 0); + gcry_sexp_release (l1); + if (!s_top) + die ("private-key part not found in result\n"); + + for (idx=0; parmlist[idx]; idx++) + { + l1 = gcry_sexp_find_token (s_top, parmlist+idx, 1); + mpi = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l1); + if (!mpi) + die ("parameter %c missing in private-key\n", parmlist[idx]); + print_mpi_line (mpi, 1); + gcry_mpi_release (mpi); + } + + gcry_sexp_release (s_top); + gcry_sexp_release (s_key); +} + + + +static size_t +compute_tag_length (size_t n) +{ + int needed = 0; + + if (n < 128) + needed += 2; /* Tag and one length byte. */ + else if (n < 256) + needed += 3; /* Tag, number of length bytes, 1 length byte. */ + else if (n < 65536) + needed += 4; /* Tag, number of length bytes, 2 length bytes. */ + else + die ("DER object too long to encode\n"); + + return needed; +} + +static unsigned char * +store_tag_length (unsigned char *p, int tag, size_t n) +{ + if (tag == TAG_SEQUENCE) + tag |= 0x20; /* constructed */ + + *p++ = tag; + if (n < 128) + *p++ = n; + else if (n < 256) + { + *p++ = 0x81; + *p++ = n; + } + else if (n < 65536) + { + *p++ = 0x82; + *p++ = n >> 8; + *p++ = n; + } + + return p; +} + + +/* Generate an RSA key of size KEYSIZE using the public exponent + PUBEXP and print it to stdout in the OpenSSL format. The format + is: + + SEQUENCE { + INTEGER (0) -- Unknown constant. + INTEGER -- n + INTEGER -- e + INTEGER -- d + INTEGER -- p + INTEGER -- q (with p < q) + INTEGER -- dmp1 = d mod (p-1) + INTEGER -- dmq1 = d mod (q-1) + INTEGER -- u = p^{-1} mod q + } + +*/ +static void +run_rsa_gen (int keysize, int pubexp) +{ + gpg_error_t err; + gcry_sexp_t keyspec, key, l1; + const char keyelems[] = "nedpq..u"; + gcry_mpi_t keyparms[8]; + size_t keyparmslen[8]; + int idx; + size_t derlen, needed, n; + unsigned char *derbuf, *der; + + err = gcry_sexp_build (&keyspec, NULL, + "(genkey (rsa (nbits %d)(rsa-use-e %d)))", + keysize, pubexp); + if (err) + die ("gcry_sexp_build failed for RSA key generation: %s\n", + gpg_strerror (err)); + + err = gcry_pk_genkey (&key, keyspec); + if (err) + die ("gcry_pk_genkey failed for RSA: %s\n", gpg_strerror (err)); + + gcry_sexp_release (keyspec); + + l1 = gcry_sexp_find_token (key, "private-key", 0); + if (!l1) + die ("private key not found in genkey result\n"); + gcry_sexp_release (key); + key = l1; + + l1 = gcry_sexp_find_token (key, "rsa", 0); + if (!l1) + die ("returned private key not formed as expected\n"); + gcry_sexp_release (key); + key = l1; + + /* Extract the parameters from the S-expression and store them in a + well defined order in KEYPARMS. */ + for (idx=0; idx < DIM(keyparms); idx++) + { + if (keyelems[idx] == '.') + { + keyparms[idx] = gcry_mpi_new (0); + continue; + } + l1 = gcry_sexp_find_token (key, keyelems+idx, 1); + if (!l1) + die ("no %c parameter in returned private key\n", keyelems[idx]); + keyparms[idx] = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); + if (!keyparms[idx]) + die ("no value for %c parameter in returned private key\n", + keyelems[idx]); + gcry_sexp_release (l1); + } + + gcry_sexp_release (key); + + /* Check that p < q; if not swap p and q and recompute u. */ + if (gcry_mpi_cmp (keyparms[3], keyparms[4]) > 0) + { + gcry_mpi_swap (keyparms[3], keyparms[4]); + gcry_mpi_invm (keyparms[7], keyparms[3], keyparms[4]); + } + + /* Compute the additional parameters. */ + gcry_mpi_sub_ui (keyparms[5], keyparms[3], 1); + gcry_mpi_mod (keyparms[5], keyparms[2], keyparms[5]); + gcry_mpi_sub_ui (keyparms[6], keyparms[4], 1); + gcry_mpi_mod (keyparms[6], keyparms[2], keyparms[6]); + + /* Compute the length of the DER encoding. */ + needed = compute_tag_length (1) + 1; + for (idx=0; idx < DIM(keyparms); idx++) + { + err = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, keyparms[idx]); + if (err) + die ("error formatting parameter: %s\n", gpg_strerror (err)); + keyparmslen[idx] = n; + needed += compute_tag_length (n) + n; + } + + /* Store the key parameters. */ + derlen = compute_tag_length (needed) + needed; + der = derbuf = gcry_xmalloc (derlen); + + der = store_tag_length (der, TAG_SEQUENCE, needed); + der = store_tag_length (der, TAG_INTEGER, 1); + *der++ = 0; + for (idx=0; idx < DIM(keyparms); idx++) + { + der = store_tag_length (der, TAG_INTEGER, keyparmslen[idx]); + err = gcry_mpi_print (GCRYMPI_FMT_STD, der, + keyparmslen[idx], NULL, keyparms[idx]); + if (err) + die ("error formatting parameter: %s\n", gpg_strerror (err)); + der += keyparmslen[idx]; + } + + /* Print the stuff. */ + for (idx=0; idx < DIM(keyparms); idx++) + gcry_mpi_release (keyparms[idx]); + + assert (der - derbuf == derlen); + + if (base64_output) + puts ("-----BEGIN RSA PRIVATE KEY-----"); + print_buffer (derbuf, derlen); + if (base64_output) + puts ("-----END RSA PRIVATE KEY-----"); + + gcry_free (derbuf); +} + + + +/* Sign DATA of length DATALEN using the key taken from the PEM + encoded KEYFILE and the hash algorithm HASHALGO. */ +static void +run_rsa_sign (const void *data, size_t datalen, + int hashalgo, int pkcs1, int pss, const char *keyfile) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig, s_tmp; + gcry_mpi_t sig_mpi = NULL; + unsigned char *outbuf; + size_t outlen; + +/* showhex ("D", data, datalen); */ + if (pkcs1) + { + unsigned char hash[64]; + unsigned int hashsize; + + hashsize = gcry_md_get_algo_dlen (hashalgo); + if (!hashsize || hashsize > sizeof hash) + die ("digest too long for buffer or unknown hash algorithm\n"); + gcry_md_hash_buffer (hashalgo, hash, data, datalen); + err = gcry_sexp_build (&s_data, NULL, + "(data (flags pkcs1)(hash %s %b))", + gcry_md_algo_name (hashalgo), + (int)hashsize, hash); + } + else if (pss) + { + unsigned char hash[64]; + unsigned int hashsize; + + hashsize = gcry_md_get_algo_dlen (hashalgo); + if (!hashsize || hashsize > sizeof hash) + die ("digest too long for buffer or unknown hash algorithm\n"); + gcry_md_hash_buffer (hashalgo, hash, data, datalen); + err = gcry_sexp_build (&s_data, NULL, + "(data (flags pss)(salt-length #00#)(hash %s %b))", + gcry_md_algo_name (hashalgo), + (int)hashsize, hash); + } + else + { + gcry_mpi_t tmp; + + err = gcry_mpi_scan (&tmp, GCRYMPI_FMT_USG, data, datalen,NULL); + if (!err) + { + err = gcry_sexp_build (&s_data, NULL, + "(data (flags raw)(value %m))", tmp); + gcry_mpi_release (tmp); + } + } + if (err) + die ("gcry_sexp_build failed for RSA data input: %s\n", + gpg_strerror (err)); + + s_key = read_private_key_file (keyfile, 0); + + err = gcry_pk_sign (&s_sig, s_data, s_key); + if (err) + { + gcry_sexp_release (read_private_key_file (keyfile, 1)); + die ("gcry_pk_signed failed (datalen=%d,keyfile=%s): %s\n", + (int)datalen, keyfile, gpg_strerror (err)); + } + gcry_sexp_release (s_key); + gcry_sexp_release (s_data); + + s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0); + if (s_tmp) + { + gcry_sexp_release (s_sig); + s_sig = s_tmp; + s_tmp = gcry_sexp_find_token (s_sig, "rsa", 0); + if (s_tmp) + { + gcry_sexp_release (s_sig); + s_sig = s_tmp; + s_tmp = gcry_sexp_find_token (s_sig, "s", 0); + if (s_tmp) + { + gcry_sexp_release (s_sig); + s_sig = s_tmp; + sig_mpi = gcry_sexp_nth_mpi (s_sig, 1, GCRYMPI_FMT_USG); + } + } + } + gcry_sexp_release (s_sig); + + if (!sig_mpi) + die ("no value in returned S-expression\n"); + err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &outbuf, &outlen, sig_mpi); + if (err) + die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err)); + gcry_mpi_release (sig_mpi); + + print_buffer (outbuf, outlen); + gcry_free (outbuf); +} + + + +/* Verify DATA of length DATALEN using the public key taken from the + PEM encoded KEYFILE and the hash algorithm HASHALGO against the + binary signature in SIGFILE. */ +static void +run_rsa_verify (const void *data, size_t datalen, int hashalgo, int pkcs1, + int pss, const char *keyfile, const char *sigfile) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig; + + if (pkcs1) + { + unsigned char hash[64]; + unsigned int hashsize; + + hashsize = gcry_md_get_algo_dlen (hashalgo); + if (!hashsize || hashsize > sizeof hash) + die ("digest too long for buffer or unknown hash algorithm\n"); + gcry_md_hash_buffer (hashalgo, hash, data, datalen); + err = gcry_sexp_build (&s_data, NULL, + "(data (flags pkcs1)(hash %s %b))", + gcry_md_algo_name (hashalgo), + (int)hashsize, hash); + } + else if (pss) + { + unsigned char hash[64]; + unsigned int hashsize; + + hashsize = gcry_md_get_algo_dlen (hashalgo); + if (!hashsize || hashsize > sizeof hash) + die ("digest too long for buffer or unknown hash algorithm\n"); + gcry_md_hash_buffer (hashalgo, hash, data, datalen); + err = gcry_sexp_build (&s_data, NULL, + "(data (flags pss)(salt-length #00#)(hash %s %b))", + gcry_md_algo_name (hashalgo), + (int)hashsize, hash); + } + else + { + gcry_mpi_t tmp; + + err = gcry_mpi_scan (&tmp, GCRYMPI_FMT_USG, data, datalen,NULL); + if (!err) + { + err = gcry_sexp_build (&s_data, NULL, + "(data (flags raw)(value %m))", tmp); + gcry_mpi_release (tmp); + } + } + if (err) + die ("gcry_sexp_build failed for RSA data input: %s\n", + gpg_strerror (err)); + + s_key = read_public_key_file (keyfile, 0); + + s_sig = read_sig_file (sigfile); + + err = gcry_pk_verify (s_sig, s_data, s_key); + if (!err) + puts ("GOOD signature"); + else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE) + puts ("BAD signature"); + else + printf ("ERROR (%s)\n", gpg_strerror (err)); + + gcry_sexp_release (s_sig); + gcry_sexp_release (s_key); + gcry_sexp_release (s_data); +} + + + +/* Generate a DSA key of size KEYSIZE and return the complete + S-expression. */ +static gcry_sexp_t +dsa_gen (int keysize) +{ + gpg_error_t err; + gcry_sexp_t keyspec, key; + + err = gcry_sexp_build (&keyspec, NULL, + "(genkey (dsa (nbits %d)(use-fips186-2)))", + keysize); + if (err) + die ("gcry_sexp_build failed for DSA key generation: %s\n", + gpg_strerror (err)); + + err = gcry_pk_genkey (&key, keyspec); + if (err) + die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err)); + + gcry_sexp_release (keyspec); + + return key; +} + + +/* Generate a DSA key of size KEYSIZE and return the complete + S-expression. */ +static gcry_sexp_t +dsa_gen_with_seed (int keysize, const void *seed, size_t seedlen) +{ + gpg_error_t err; + gcry_sexp_t keyspec, key; + + err = gcry_sexp_build (&keyspec, NULL, + "(genkey" + " (dsa" + " (nbits %d)" + " (use-fips186-2)" + " (derive-parms" + " (seed %b))))", + keysize, (int)seedlen, seed); + if (err) + die ("gcry_sexp_build failed for DSA key generation: %s\n", + gpg_strerror (err)); + + err = gcry_pk_genkey (&key, keyspec); + if (err) + die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err)); + + gcry_sexp_release (keyspec); + + return key; +} + + +/* Generate an ECDSA key on the specified curve and return the complete + S-expression. */ +static gcry_sexp_t +ecdsa_gen_key (const char *curve) +{ + gpg_error_t err; + gcry_sexp_t keyspec, key; + + err = gcry_sexp_build (&keyspec, NULL, + "(genkey" + " (ecc" + " (use-fips186)" + " (curve %s)))", + curve); + if (err) + die ("gcry_sexp_build failed for ECDSA key generation: %s\n", + gpg_strerror (err)); + err = gcry_pk_genkey (&key, keyspec); + if (err) + die ("gcry_pk_genkey failed for ECDSA: %s\n", gpg_strerror (err)); + + gcry_sexp_release (keyspec); + + return key; +} + + +/* Print the domain parameter as well as the derive information. KEY + is the complete key as returned by dsa_gen. We print to stdout + with one parameter per line in hex format using this order: p, q, + g, seed, counter, h. */ +static void +print_dsa_domain_parameters (gcry_sexp_t key) +{ + gcry_sexp_t l1, l2; + gcry_mpi_t mpi; + int idx; + const void *data; + size_t datalen; + char *string; + + l1 = gcry_sexp_find_token (key, "public-key", 0); + if (!l1) + die ("public key not found in genkey result\n"); + + l2 = gcry_sexp_find_token (l1, "dsa", 0); + if (!l2) + die ("returned public key not formed as expected\n"); + gcry_sexp_release (l1); + l1 = l2; + + /* Extract the parameters from the S-expression and print them to stdout. */ + for (idx=0; "pqg"[idx]; idx++) + { + l2 = gcry_sexp_find_token (l1, "pqg"+idx, 1); + if (!l2) + die ("no %c parameter in returned public key\n", "pqg"[idx]); + mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + if (!mpi) + die ("no value for %c parameter in returned public key\n","pqg"[idx]); + gcry_sexp_release (l2); + if (standalone_mode) + printf ("%c = ", "PQG"[idx]); + print_mpi_line (mpi, 1); + gcry_mpi_release (mpi); + } + gcry_sexp_release (l1); + + /* Extract the seed values. */ + l1 = gcry_sexp_find_token (key, "misc-key-info", 0); + if (!l1) + die ("misc-key-info not found in genkey result\n"); + + l2 = gcry_sexp_find_token (l1, "seed-values", 0); + if (!l2) + die ("no seed-values in returned key\n"); + gcry_sexp_release (l1); + l1 = l2; + + l2 = gcry_sexp_find_token (l1, "seed", 0); + if (!l2) + die ("no seed value in returned key\n"); + data = gcry_sexp_nth_data (l2, 1, &datalen); + if (!data) + die ("no seed value in returned key\n"); + if (standalone_mode) + printf ("Seed = "); + print_data_line (data, datalen); + gcry_sexp_release (l2); + + l2 = gcry_sexp_find_token (l1, "counter", 0); + if (!l2) + die ("no counter value in returned key\n"); + string = gcry_sexp_nth_string (l2, 1); + if (!string) + die ("no counter value in returned key\n"); + if (standalone_mode) + printf ("c = %ld\n", strtoul (string, NULL, 10)); + else + printf ("%lX\n", strtoul (string, NULL, 10)); + gcry_free (string); + gcry_sexp_release (l2); + + l2 = gcry_sexp_find_token (l1, "h", 0); + if (!l2) + die ("no n value in returned key\n"); + mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + if (!mpi) + die ("no h value in returned key\n"); + if (standalone_mode) + printf ("H = "); + print_mpi_line (mpi, 1); + gcry_mpi_release (mpi); + gcry_sexp_release (l2); + + gcry_sexp_release (l1); +} + + +/* Print public key Q (in octet-string format) and private key d. + KEY is the complete key as returned by ecdsa_gen_key. + with one parameter per line in hex format using this order: d, Q. */ +static void +print_ecdsa_dq (gcry_sexp_t key) +{ + gcry_sexp_t l1, l2; + gcry_mpi_t mpi; + int idx; + + l1 = gcry_sexp_find_token (key, "private-key", 0); + if (!l1) + die ("private key not found in genkey result\n"); + + l2 = gcry_sexp_find_token (l1, "ecc", 0); + if (!l2) + die ("returned private key not formed as expected\n"); + gcry_sexp_release (l1); + l1 = l2; + + /* Extract the parameters from the S-expression and print them to stdout. */ + for (idx=0; "dq"[idx]; idx++) + { + l2 = gcry_sexp_find_token (l1, "dq"+idx, 1); + if (!l2) + die ("no %c parameter in returned public key\n", "dq"[idx]); + mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + if (!mpi) + die ("no value for %c parameter in returned private key\n","dq"[idx]); + gcry_sexp_release (l2); + if (standalone_mode) + printf ("%c = ", "dQ"[idx]); + print_mpi_line (mpi, 1); + gcry_mpi_release (mpi); + } + + gcry_sexp_release (l1); +} + + +/* Generate DSA domain parameters for a modulus size of KEYSIZE. The + result is printed to stdout with one parameter per line in hex + format and in this order: p, q, g, seed, counter, h. If SEED is + not NULL this seed value will be used for the generation. */ +static void +run_dsa_pqg_gen (int keysize, const void *seed, size_t seedlen) +{ + gcry_sexp_t key; + + if (seed) + key = dsa_gen_with_seed (keysize, seed, seedlen); + else + key = dsa_gen (keysize); + print_dsa_domain_parameters (key); + gcry_sexp_release (key); +} + + +/* Generate a DSA key of size of KEYSIZE and write the private key to + FILENAME. Also write the parameters to stdout in the same way as + run_dsa_pqg_gen. */ +static void +run_dsa_gen (int keysize, const char *filename) +{ + gcry_sexp_t key, private_key; + FILE *fp; + + key = dsa_gen (keysize); + private_key = gcry_sexp_find_token (key, "private-key", 0); + if (!private_key) + die ("private key not found in genkey result\n"); + print_dsa_domain_parameters (key); + + fp = fopen (filename, "wb"); + if (!fp) + die ("can't create `%s': %s\n", filename, strerror (errno)); + print_sexp (private_key, fp); + fclose (fp); + + gcry_sexp_release (private_key); + gcry_sexp_release (key); +} + + + +/* Sign DATA of length DATALEN using the key taken from the S-expression + encoded KEYFILE. */ +static void +run_dsa_sign (const void *data, size_t datalen, const char *keyfile) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig, s_tmp, s_tmp2; + char hash[20]; + gcry_mpi_t tmpmpi; + + gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen); + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL); + if (!err) + { + err = gcry_sexp_build (&s_data, NULL, + "(data (flags raw)(value %m))", tmpmpi); + gcry_mpi_release (tmpmpi); + } + if (err) + die ("gcry_sexp_build failed for DSA data input: %s\n", + gpg_strerror (err)); + + s_key = read_sexp_from_file (keyfile); + + err = gcry_pk_sign (&s_sig, s_data, s_key); + if (err) + { + gcry_sexp_release (read_private_key_file (keyfile, 1)); + die ("gcry_pk_signed failed (datalen=%d,keyfile=%s): %s\n", + (int)datalen, keyfile, gpg_strerror (err)); + } + gcry_sexp_release (s_data); + + /* We need to return the Y parameter first. */ + s_tmp = gcry_sexp_find_token (s_key, "private-key", 0); + if (!s_tmp) + die ("private key part not found in provided key\n"); + + s_tmp2 = gcry_sexp_find_token (s_tmp, "dsa", 0); + if (!s_tmp2) + die ("private key part is not a DSA key\n"); + gcry_sexp_release (s_tmp); + + s_tmp = gcry_sexp_find_token (s_tmp2, "y", 0); + tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG); + if (!tmpmpi) + die ("no y parameter in DSA key\n"); + print_mpi_line (tmpmpi, 1); + gcry_mpi_release (tmpmpi); + gcry_sexp_release (s_tmp); + + gcry_sexp_release (s_key); + + + /* Now return the actual signature. */ + s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0); + if (!s_tmp) + die ("no sig-val element in returned S-expression\n"); + + gcry_sexp_release (s_sig); + s_sig = s_tmp; + s_tmp = gcry_sexp_find_token (s_sig, "dsa", 0); + if (!s_tmp) + die ("no dsa element in returned S-expression\n"); + + gcry_sexp_release (s_sig); + s_sig = s_tmp; + + s_tmp = gcry_sexp_find_token (s_sig, "r", 0); + tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG); + if (!tmpmpi) + die ("no r parameter in returned S-expression\n"); + print_mpi_line (tmpmpi, 1); + gcry_mpi_release (tmpmpi); + gcry_sexp_release (s_tmp); + + s_tmp = gcry_sexp_find_token (s_sig, "s", 0); + tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG); + if (!tmpmpi) + die ("no s parameter in returned S-expression\n"); + print_mpi_line (tmpmpi, 1); + gcry_mpi_release (tmpmpi); + gcry_sexp_release (s_tmp); + + gcry_sexp_release (s_sig); +} + + + +/* Verify DATA of length DATALEN using the public key taken from the + S-expression in KEYFILE against the S-expression formatted + signature in SIGFILE. */ +static void +run_dsa_verify (const void *data, size_t datalen, + const char *keyfile, const char *sigfile) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig; + char hash[20]; + gcry_mpi_t tmpmpi; + + gcry_md_hash_buffer (GCRY_MD_SHA1, hash, data, datalen); + /* Note that we can't simply use %b with HASH to build the + S-expression, because that might yield a negative value. */ + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, 20, NULL); + if (!err) + { + err = gcry_sexp_build (&s_data, NULL, + "(data (flags raw)(value %m))", tmpmpi); + gcry_mpi_release (tmpmpi); + } + if (err) + die ("gcry_sexp_build failed for DSA data input: %s\n", + gpg_strerror (err)); + + s_key = read_sexp_from_file (keyfile); + s_sig = read_sexp_from_file (sigfile); + + err = gcry_pk_verify (s_sig, s_data, s_key); + if (!err) + puts ("GOOD signature"); + else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE) + puts ("BAD signature"); + else + printf ("ERROR (%s)\n", gpg_strerror (err)); + + gcry_sexp_release (s_sig); + gcry_sexp_release (s_key); + gcry_sexp_release (s_data); +} + + + +/* Sign DATA of length DATALEN using the key taken from the S-expression + encoded KEYFILE. */ +static void +run_ecdsa_sign (const void *data, size_t datalen, + const char *keyfile, const int algo) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig, s_tmp; + char hash[128]; + gcry_mpi_t tmpmpi; + + s_key = read_sexp_from_file (keyfile); + + gcry_md_hash_buffer (algo, hash, data, datalen); + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, + gcry_md_get_algo_dlen(algo), NULL); + if (!err) + { + err = gcry_sexp_build (&s_data, NULL, + "(data (flags raw)(hash %s %M))", + gcry_md_algo_name(algo), tmpmpi); + gcry_mpi_release (tmpmpi); + } + if (err) + die ("gcry_sexp_build failed for ECDSA data input: %s\n", + gpg_strerror (err)); + + err = gcry_pk_sign (&s_sig, s_data, s_key); + if (err) + { + die ("gcry_pk_signed failed: %s\n", gpg_strerror (err)); + } + gcry_sexp_release (s_data); + gcry_sexp_release (s_key); + + /* Now return the actual signature. */ + s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0); + if (!s_tmp) + die ("no sig-val element in returned S-expression\n"); + + gcry_sexp_release (s_sig); + s_sig = s_tmp; + s_tmp = gcry_sexp_find_token (s_sig, "ecdsa", 0); + if (!s_tmp) + die ("no ecdsa element in returned S-expression\n"); + + gcry_sexp_release (s_sig); + s_sig = s_tmp; + + s_tmp = gcry_sexp_find_token (s_sig, "r", 0); + tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG); + if (!tmpmpi) + die ("no r parameter in returned S-expression\n"); + print_mpi_line (tmpmpi, 1); + gcry_mpi_release (tmpmpi); + gcry_sexp_release (s_tmp); + + s_tmp = gcry_sexp_find_token (s_sig, "s", 0); + tmpmpi = gcry_sexp_nth_mpi (s_tmp, 1, GCRYMPI_FMT_USG); + if (!tmpmpi) + die ("no s parameter in returned S-expression\n"); + print_mpi_line (tmpmpi, 1); + gcry_mpi_release (tmpmpi); + gcry_sexp_release (s_tmp); + + gcry_sexp_release (s_sig); +} + + + +/* Verify DATA of length DATALEN using the public key taken from the + S-expression in KEYFILE against the S-expression formatted + signature in SIGFILE. */ +static void +run_ecdsa_verify (const void *data, size_t datalen, + const char *keyfile, const int algo, const char *sigfile) + +{ + gpg_error_t err; + gcry_sexp_t s_data, s_key, s_sig; + char hash[128]; + gcry_mpi_t tmpmpi; + + s_key = read_sexp_from_file (keyfile); + + gcry_md_hash_buffer (algo, hash, data, datalen); + /* Note that we can't simply use %b with HASH to build the + S-expression, because that might yield a negative value. */ + err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, hash, + gcry_md_get_algo_dlen(algo), NULL); + if (!err) + { + err = gcry_sexp_build (&s_data, NULL, + "(data (flags raw)(hash %s %M))", + gcry_md_algo_name(algo), tmpmpi); + gcry_mpi_release (tmpmpi); + } + if (err) + die ("gcry_sexp_build failed for DSA data input: %s\n", + gpg_strerror (err)); + + s_sig = read_sexp_from_file (sigfile); + + err = gcry_pk_verify (s_sig, s_data, s_key); + if (!err) + puts ("GOOD signature"); + else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE) + puts ("BAD signature"); + else + printf ("ERROR (%s)\n", gpg_strerror (err)); + + gcry_sexp_release (s_sig); + gcry_sexp_release (s_key); + gcry_sexp_release (s_data); +} + + +/* Generate an ECDSA key with specified domain parameters + and print the d and Q values, in the standard octet-string format. */ +static void +run_ecdsa_gen_key (const char *curve) +{ + gcry_sexp_t key; + + key = ecdsa_gen_key (curve); + print_ecdsa_dq (key); + + gcry_sexp_release (key); +} + + + +static void +usage (int show_help) +{ + if (!show_help) + { + fputs ("usage: " PGM + " [OPTION] [FILE] (try --help for more information)\n", stderr); + exit (2); + } + fputs + ("Usage: " PGM " [OPTIONS] MODE [FILE]\n" + "Run a crypto operation using hex encoded input and output.\n" + "MODE:\n" + " encrypt, decrypt, digest, random, hmac-sha,\n" + " rsa-{derive,gen,sign,verify},\n" + " dsa-{pqg-gen,gen,sign,verify}, ecdsa-{gen-key,sign,verify}\n" + "OPTIONS:\n" + " --verbose Print additional information\n" + " --binary Input and output is in binary form\n" + " --no-fips Do not force FIPS mode\n" + " --key KEY Use the hex encoded KEY\n" + " --iv IV Use the hex encoded IV\n" + " --dt DT Use the hex encoded DT for the RNG\n" + " --algo NAME Use algorithm NAME\n" + " --curve NAME Select ECC curve spec NAME\n" + " --keysize N Use a keysize of N bits\n" + " --signature NAME Take signature from file NAME\n" + " --chunk N Read in chunks of N bytes (implies --binary)\n" + " --pkcs1 Use PKCS#1 encoding\n" + " --pss Use PSS encoding with a zero length salt\n" + " --mct-server Run a monte carlo test server\n" + " --loop Enable random loop mode\n" + " --progress Print pogress indicators\n" + " --help Print this text\n" + "With no FILE, or when FILE is -, read standard input.\n" + "Report bugs to " PACKAGE_BUGREPORT ".\n" , stdout); + exit (0); +} + +int +main (int argc, char **argv) +{ + int last_argc = -1; + gpg_error_t err; + int no_fips = 0; + int progress = 0; + int use_pkcs1 = 0; + int use_pss = 0; + const char *mode_string; + const char *curve_string = NULL; + const char *key_string = NULL; + const char *iv_string = NULL; + const char *dt_string = NULL; + const char *algo_string = NULL; + const char *keysize_string = NULL; + const char *signature_string = NULL; + FILE *input; + void *data; + size_t datalen; + size_t chunksize = 0; + int mct_server = 0; + + + if (argc) + { argc--; argv++; } + + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + usage (1); + } + else if (!strcmp (*argv, "--version")) + { + fputs (PGM " (Libgcrypt) " PACKAGE_VERSION "\n", stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--binary")) + { + binary_input = binary_output = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--no-fips")) + { + no_fips++; + argc--; argv++; + } + else if (!strcmp (*argv, "--loop")) + { + loop_mode = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--progress")) + { + progress = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--key")) + { + argc--; argv++; + if (!argc) + usage (0); + key_string = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--iv")) + { + argc--; argv++; + if (!argc) + usage (0); + iv_string = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--dt")) + { + argc--; argv++; + if (!argc) + usage (0); + dt_string = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--algo")) + { + argc--; argv++; + if (!argc) + usage (0); + algo_string = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--keysize")) + { + argc--; argv++; + if (!argc) + usage (0); + keysize_string = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--signature")) + { + argc--; argv++; + if (!argc) + usage (0); + signature_string = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--chunk")) + { + argc--; argv++; + if (!argc) + usage (0); + chunksize = atoi (*argv); + binary_input = binary_output = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--curve")) + { + argc--; argv++; + if (!argc) + usage (0); + curve_string = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--pkcs1")) + { + use_pkcs1 = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--pss")) + { + use_pss = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--mct-server")) + { + mct_server = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--standalone")) + { + standalone_mode = 1; + argc--; argv++; + } + } + + if (!argc || argc > 2) + usage (0); + + mode_string = *argv; + + if (use_pkcs1 && use_pss) + die ("Only one of --pkcs or --pss may be given\n"); + + if (!strcmp (mode_string, "rsa-derive")) + binary_input = 1; + + if (argc == 2 && strcmp (argv[1], "-")) + { + input = fopen (argv[1], binary_input? "rb":"r"); + if (!input) + die ("can't open `%s': %s\n", argv[1], strerror (errno)); + } + else + input = stdin; + +#ifndef HAVE_W32_SYSTEM + if (loop_mode) + signal (SIGPIPE, SIG_IGN); +#endif + + if (verbose) + fprintf (stderr, PGM ": started (mode=%s)\n", mode_string); + + xgcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose); + if (!no_fips) + xgcry_control (GCRYCTL_FORCE_FIPS_MODE, 0); + if (!gcry_check_version ("1.4.3")) + die ("Libgcrypt is not sufficient enough\n"); + if (verbose) + fprintf (stderr, PGM ": using Libgcrypt %s\n", gcry_check_version (NULL)); + if (no_fips) + xgcry_control (GCRYCTL_DISABLE_SECMEM, 0); + xgcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + + /* Most operations need some input data. */ + if (!chunksize + && !mct_server + && strcmp (mode_string, "random") + && strcmp (mode_string, "rsa-gen") + && strcmp (mode_string, "rsa-keygen") + && strcmp (mode_string, "rsa-keygen-kat") + && strcmp (mode_string, "dsa-gen") + && strcmp (mode_string, "ecdsa-gen-key") ) + { + data = read_file (input, !binary_input, &datalen); + if (!data) + die ("error reading%s input\n", binary_input?"":" and decoding"); + if (verbose) + fprintf (stderr, PGM ": %u bytes of input data\n", + (unsigned int)datalen); + } + else + { + data = NULL; + datalen = 0; + } + + + if (!strcmp (mode_string, "encrypt") || !strcmp (mode_string, "decrypt")) + { + int cipher_algo, cipher_mode; + void *iv_buffer = NULL; + void *key_buffer = NULL; + size_t iv_buflen, key_buflen; + + if (!algo_string) + die ("option --algo is required in this mode\n"); + cipher_algo = map_openssl_cipher_name (algo_string, &cipher_mode); + if (!cipher_algo) + die ("cipher algorithm `%s' is not supported\n", algo_string); + if (mct_server) + { + int iterations; + + for (;;) + { + gcry_free (key_buffer); key_buffer = NULL; + gcry_free (iv_buffer); iv_buffer = NULL; + gcry_free (data); data = NULL; + if (!(key_buffer = read_textline (input))) + { + if (feof (input)) + break; + die ("no version info in input\n"); + } + if (atoi (key_buffer) != 1) + die ("unsupported input version %s\n", + (const char*)key_buffer); + gcry_free (key_buffer); + if (!(key_buffer = read_textline (input))) + die ("no iteration count in input\n"); + iterations = atoi (key_buffer); + gcry_free (key_buffer); + if (!(key_buffer = read_hexline (input, &key_buflen))) + die ("no key in input\n"); + if (!(iv_buffer = read_hexline (input, &iv_buflen))) + die ("no IV in input\n"); + if (!(data = read_hexline (input, &datalen))) + die ("no data in input\n"); + skip_to_empty_line (input); + + run_cipher_mct_loop ((*mode_string == 'e'), + cipher_algo, cipher_mode, + iv_buffer, iv_buflen, + key_buffer, key_buflen, + data, datalen, iterations); + } + } + else + { + if (cipher_mode != GCRY_CIPHER_MODE_ECB) + { + if (!iv_string) + die ("option --iv is required in this mode\n"); + iv_buffer = hex2buffer (iv_string, &iv_buflen); + if (!iv_buffer) + die ("invalid value for IV\n"); + } + else + { + iv_buffer = NULL; + iv_buflen = 0; + } + if (!key_string) + die ("option --key is required in this mode\n"); + key_buffer = hex2buffer (key_string, &key_buflen); + if (!key_buffer) + die ("invalid value for KEY\n"); + + run_encrypt_decrypt ((*mode_string == 'e'), + cipher_algo, cipher_mode, + iv_buffer, iv_buflen, + key_buffer, key_buflen, + data, data? datalen:chunksize, input); + } + gcry_free (key_buffer); + gcry_free (iv_buffer); + } + else if (!strcmp (mode_string, "digest")) + { + int algo; + + if (!algo_string) + die ("option --algo is required in this mode\n"); + algo = gcry_md_map_name (algo_string); + if (!algo) + die ("digest algorithm `%s' is not supported\n", algo_string); + if (!data) + die ("no data available (do not use --chunk)\n"); + + run_digest (algo, data, datalen); + } + else if (!strcmp (mode_string, "random")) + { + void *context; + unsigned char key[16]; + unsigned char seed[16]; + unsigned char dt[16]; + unsigned char buffer[16]; + size_t count = 0; + + if (!key_string || hex2bin (key_string, key, 16) < 0 ) + die ("value for --key are not 32 hex digits\n"); + if (!iv_string || hex2bin (iv_string, seed, 16) < 0 ) + die ("value for --iv are not 32 hex digits\n"); + if (!dt_string || hex2bin (dt_string, dt, 16) < 0 ) + die ("value for --dt are not 32 hex digits\n"); + + /* The flag value 1 disables the dup check, so that the RNG + returns all generated data. */ + err = init_external_rng_test (&context, 1, key, 16, seed, 16, dt, 16); + if (err) + die ("init external RNG test failed: %s\n", gpg_strerror (err)); + + do + { + err = run_external_rng_test (context, buffer, sizeof buffer); + if (err) + die ("running external RNG test failed: %s\n", gpg_strerror (err)); + print_buffer (buffer, sizeof buffer); + if (progress) + { + if (!(++count % 1000)) + fprintf (stderr, PGM ": %lu random bytes so far\n", + (unsigned long int)(count * sizeof buffer)); + } + } + while (loop_mode); + + if (progress) + fprintf (stderr, PGM ": %lu random bytes\n", + (unsigned long int)(count * sizeof buffer)); + + deinit_external_rng_test (context); + } + else if (!strcmp (mode_string, "hmac-sha")) + { + int algo; + void *key_buffer; + size_t key_buflen; + + if (!data) + die ("no data available (do not use --chunk)\n"); + if (!algo_string) + die ("option --algo is required in this mode\n"); + switch (atoi (algo_string)) + { + case 1: algo = GCRY_MD_SHA1; break; + case 224: algo = GCRY_MD_SHA224; break; + case 256: algo = GCRY_MD_SHA256; break; + case 384: algo = GCRY_MD_SHA384; break; + case 512: algo = GCRY_MD_SHA512; break; + default: algo = 0; break; + } + if (!algo) + die ("no digest algorithm found for hmac type `%s'\n", algo_string); + if (!key_string) + die ("option --key is required in this mode\n"); + key_buffer = hex2buffer (key_string, &key_buflen); + if (!key_buffer) + die ("invalid value for KEY\n"); + + run_hmac (algo, key_buffer, key_buflen, data, datalen); + + gcry_free (key_buffer); + } + else if (!strcmp (mode_string, "rsa-derive")) + { + if (!data) + die ("no data available (do not use --chunk)\n"); + run_rsa_derive (data, datalen); + } + else if (!strcmp (mode_string, "rsa-keygen")) + { + data = read_file (input, 0, &datalen); + if (!data) + die ("no data available (do not use --chunk)\n"); + run_rsa_keygen (data, datalen, 0); + } + else if (!strcmp (mode_string, "rsa-keygen-kat")) + { + data = read_file (input, 0, &datalen); + if (!data) + die ("no data available (do not use --chunk)\n"); + run_rsa_keygen (data, datalen, 1); + } + else if (!strcmp (mode_string, "rsa-gen")) + { + int keysize; + + if (!binary_output) + base64_output = 1; + + keysize = keysize_string? atoi (keysize_string) : 0; + if (keysize < 128 || keysize > 16384) + die ("invalid keysize specified; needs to be 128 .. 16384\n"); + run_rsa_gen (keysize, 65537); + } + else if (!strcmp (mode_string, "rsa-sign")) + { + int algo; + + if (!key_string) + die ("option --key is required in this mode\n"); + if (access (key_string, R_OK)) + die ("option --key needs to specify an existing keyfile\n"); + if (!algo_string) + die ("option --algo is required in this mode\n"); + algo = gcry_md_map_name (algo_string); + if (!algo) + die ("digest algorithm `%s' is not supported\n", algo_string); + if (!data) + die ("no data available (do not use --chunk)\n"); + + run_rsa_sign (data, datalen, algo, use_pkcs1, use_pss, key_string); + + } + else if (!strcmp (mode_string, "rsa-verify")) + { + int algo; + + if (!key_string) + die ("option --key is required in this mode\n"); + if (access (key_string, R_OK)) + die ("option --key needs to specify an existing keyfile\n"); + if (!algo_string) + die ("option --algo is required in this mode\n"); + algo = gcry_md_map_name (algo_string); + if (!algo) + die ("digest algorithm `%s' is not supported\n", algo_string); + if (!data) + die ("no data available (do not use --chunk)\n"); + if (!signature_string) + die ("option --signature is required in this mode\n"); + if (access (signature_string, R_OK)) + die ("option --signature needs to specify an existing file\n"); + + run_rsa_verify (data, datalen, algo, use_pkcs1, use_pss, key_string, + signature_string); + + } + else if (!strcmp (mode_string, "dsa-pqg-gen")) + { + int keysize; + + keysize = keysize_string? atoi (keysize_string) : 0; + if (keysize < 1024 || keysize > 3072) + die ("invalid keysize specified; needs to be 1024 .. 3072\n"); + run_dsa_pqg_gen (keysize, datalen? data:NULL, datalen); + } + else if (!strcmp (mode_string, "dsa-gen")) + { + int keysize; + + keysize = keysize_string? atoi (keysize_string) : 0; + if (keysize < 1024 || keysize > 3072) + die ("invalid keysize specified; needs to be 1024 .. 3072\n"); + if (!key_string) + die ("option --key is required in this mode\n"); + run_dsa_gen (keysize, key_string); + } + else if (!strcmp (mode_string, "dsa-sign")) + { + if (!key_string) + die ("option --key is required in this mode\n"); + if (access (key_string, R_OK)) + die ("option --key needs to specify an existing keyfile\n"); + if (!data) + die ("no data available (do not use --chunk)\n"); + + run_dsa_sign (data, datalen, key_string); + } + else if (!strcmp (mode_string, "dsa-verify")) + { + if (!key_string) + die ("option --key is required in this mode\n"); + if (access (key_string, R_OK)) + die ("option --key needs to specify an existing keyfile\n"); + if (!data) + die ("no data available (do not use --chunk)\n"); + if (!signature_string) + die ("option --signature is required in this mode\n"); + if (access (signature_string, R_OK)) + die ("option --signature needs to specify an existing file\n"); + + run_dsa_verify (data, datalen, key_string, signature_string); + } + else if (!strcmp (mode_string, "ecdsa-gen-key")) + { + if (!curve_string) + die ("option --curve containing name of the specified curve is required in this mode\n"); + run_ecdsa_gen_key (curve_string); + } + else if (!strcmp (mode_string, "ecdsa-sign")) + { + int algo; + + if (!key_string) + die ("option --key is required in this mode\n"); + if (access (key_string, R_OK)) + die ("option --key needs to specify an existing keyfile\n"); + if (!algo_string) + die ("use --algo to specify the digest algorithm\n"); + algo = gcry_md_map_name (algo_string); + if (!algo) + die ("digest algorithm `%s' is not supported\n", algo_string); + + if (!data) + die ("no data available (do not use --chunk)\n"); + + run_ecdsa_sign (data, datalen, key_string, algo); + } + else if (!strcmp (mode_string, "ecdsa-verify")) + { + int algo; + + if (!key_string) + die ("option --key is required in this mode\n"); + if (access (key_string, R_OK)) + die ("option --key needs to specify an existing keyfile\n"); + if (!algo_string) + die ("use --algo to specify the digest algorithm\n"); + algo = gcry_md_map_name (algo_string); + if (!algo) + die ("digest algorithm `%s' is not supported\n", algo_string); + if (!data) + die ("no data available (do not use --chunk)\n"); + if (!signature_string) + die ("option --signature is required in this mode\n"); + if (access (signature_string, R_OK)) + die ("option --signature needs to specify an existing file\n"); + + run_ecdsa_verify (data, datalen, key_string, algo, signature_string); + } + else + usage (0); + + gcry_free (data); + + /* Because Libgcrypt does not enforce FIPS mode in all cases we let + the process die if Libgcrypt is not anymore in FIPS mode after + the actual operation. */ + if (!no_fips && !gcry_fips_mode_active ()) + die ("FIPS mode is not anymore active\n"); + + if (verbose) + fputs (PGM ": ready\n", stderr); + + return 0; +}