Index: libgcrypt-1.7.2/tests/cavs_driver.pl =================================================================== --- libgcrypt-1.7.2.orig/tests/cavs_driver.pl +++ libgcrypt-1.7.2/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,10 +305,20 @@ 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 @@ -500,17 +560,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); +} + +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 dsa-pqg-gen"; + 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 +594,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 +1225,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 +1291,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 +1299,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 +1324,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; @@ -1503,21 +1593,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 +1617,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 @@ -1658,12 +1901,16 @@ 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 $xp1 = ""; my $xp2 = ""; my $Xp = ""; @@ -1700,7 +1947,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|KeyPair|PQGVer)/) { if ($tmpline =~ /CBC/) { $mode="cbc"; } elsif ($tmpline =~ /ECB/) { $mode="ecb"; } elsif ($tmpline =~ /OFB/) { $mode="ofb"; } @@ -1749,7 +1996,15 @@ sub parse($$) { if ($tt == 0) { ##### Identify the test type - if ($tmpline =~ /KeyGen RSA \(X9\.31\)/) { + if ($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 +2015,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 +2047,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 +2130,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 @@ -1932,11 +2213,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 +2251,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); @@ -2074,11 +2370,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 "") { @@ -2141,6 +2436,74 @@ 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 > 0) { die "Test case $tt not defined"; } @@ -2199,7 +2562,9 @@ 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; Index: libgcrypt-1.7.2/tests/cavs_tests.sh =================================================================== --- libgcrypt-1.7.2.orig/tests/cavs_tests.sh +++ libgcrypt-1.7.2/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 Index: libgcrypt-1.7.2/tests/fipsdrv.c =================================================================== --- libgcrypt-1.7.2.orig/tests/fipsdrv.c +++ libgcrypt-1.7.2/tests/fipsdrv.c @@ -892,6 +892,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; @@ -1765,14 +1768,14 @@ run_rsa_verify (const void *data, size_t /* 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)); @@ -1790,7 +1793,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; @@ -1799,10 +1802,11 @@ dsa_gen_with_seed (int keysize, const vo "(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)); @@ -1810,6 +1814,37 @@ dsa_gen_with_seed (int keysize, const vo 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); @@ -1849,7 +1884,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; @@ -1885,6 +1920,9 @@ print_dsa_domain_parameters (gcry_sexp_t } gcry_sexp_release (l1); + if (!print_misc) + return; + /* Extract the seed values. */ l1 = gcry_sexp_find_token (key, "misc-key-info", 0); if (!l1) @@ -1976,38 +2014,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) @@ -2020,6 +2126,53 @@ run_dsa_gen (int keysize, const char *fi } +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. */ @@ -2029,11 +2182,16 @@ run_dsa_sign (const void *data, size_t d { 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, @@ -2044,8 +2202,6 @@ run_dsa_sign (const void *data, size_t d 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) { @@ -2121,13 +2277,18 @@ run_dsa_verify (const void *data, size_t { 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, @@ -2138,7 +2299,6 @@ run_dsa_verify (const void *data, size_t 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); @@ -2304,7 +2464,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" @@ -2315,6 +2475,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" @@ -2344,6 +2505,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; @@ -2437,6 +2599,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++; @@ -2792,23 +2962,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")) {