Fixed the fips 186 dsa key generation.
authorWerner Koch <wk@gnupg.org>
Fri, 28 Nov 2008 19:10:15 +0000 (19:10 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 28 Nov 2008 19:10:15 +0000 (19:10 +0000)
Allow apssing of a seed value.
Add a new regression test.
Updated the cavs driver.

cipher/ChangeLog
cipher/dsa.c
cipher/primegen.c
tests/ChangeLog
tests/Makefile.am
tests/cavs_driver.pl
tests/fips186-dsa.c [new file with mode: 0644]
tests/fipsdrv.c

index 86a2d52..84b9d4f 100644 (file)
@@ -1,3 +1,10 @@
+2008-11-28  Werner Koch  <wk@g10code.com>
+
+       * dsa.c (generate_fips186): Add arg DERIVEPARMS and use the seed
+       value if available.
+
+       * primegen.c (_gcry_generate_fips186_2_prime): Fix inner p loop.
+
 2008-11-26  Werner Koch  <wk@g10code.com>
 
        * primegen.c (_gcry_generate_fips186_3_prime): New.
index 8e68793..4093089 100644 (file)
@@ -356,15 +356,20 @@ generate (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits,
 
 /* Generate a DSA key pair with a key of size NBITS using the
    algorithm given in FIPS-186-3.  If USE_FIPS186_2 is true,
-   FIPS-186-2 is used and thus the length is restricted to
-   1024/160.  */
+   FIPS-186-2 is used and thus the length is restricted to 1024/160.
+   If DERIVEPARMS are not NULL the may contain a seed value. */
 static gpg_err_code_t
 generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits,
-                  int use_fips186_2,
+                  gcry_sexp_t deriveparms, int use_fips186_2,
                   int *r_counter, void **r_seed, size_t *r_seedlen,
                   gcry_mpi_t *r_h)
 {
   gpg_err_code_t ec;
+  struct {
+    gcry_sexp_t sexp;
+    const void *seed;
+    size_t seedlen;
+  } initial_seed = { NULL, NULL, 0 };
   gcry_mpi_t prime_q = NULL; 
   gcry_mpi_t prime_p = NULL; 
   gcry_mpi_t value_g = NULL; /* The generator. */
@@ -403,10 +408,22 @@ generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits,
   else
     return GPG_ERR_INV_VALUE;
 
+  /* Get an initial seed value.  */
+  if (deriveparms)
+    {
+      initial_seed.sexp = gcry_sexp_find_token (deriveparms, "seed", 0);
+      if (initial_seed.sexp)
+        initial_seed.seed = gcry_sexp_nth_data (initial_seed.sexp, 1,
+                                                &initial_seed.seedlen);
+    }
+
   /* Fixme: Enable 186-3 after it has been approved and after fixing
-     the generation fucntion.  */
+     the generation function.  */
 /*   if (use_fips186_2) */
-    ec = _gcry_generate_fips186_2_prime (nbits, qbits, NULL, 0,
+  (void)use_fips186_2;
+    ec = _gcry_generate_fips186_2_prime (nbits, qbits, 
+                                         initial_seed.seed, 
+                                         initial_seed.seedlen,
                                          &prime_q, &prime_p, 
                                          r_counter,
                                          r_seed, r_seedlen);
@@ -415,6 +432,7 @@ generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits,
 /*                                          &prime_q, &prime_p, */
 /*                                          r_counter, */
 /*                                          r_seed, r_seedlen, NULL); */
+  gcry_sexp_release (initial_seed.sexp);
   if (ec)
     goto leave;
 
@@ -669,7 +687,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue,
       size_t seedlen;
       gcry_mpi_t h_value;
 
-      ec = generate_fips186 (&sk, nbits, qbits, use_fips186_2,
+      ec = generate_fips186 (&sk, nbits, qbits, deriveparms, use_fips186_2,
                              &counter, &seed, &seedlen, &h_value);
       gcry_sexp_release (deriveparms);
       if (!ec)
index 1d8aba8..b869bee 100644 (file)
@@ -1549,7 +1549,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits,
           if (ec)
             goto leave;
           if (value_k == value_n)
-            mpi_clear_highbit (tmpval, value_b+1); /* (V_n mod 2^b) */
+            mpi_clear_highbit (tmpval, value_b); /* (V_n mod 2^b) */
           mpi_lshift (tmpval, tmpval, value_k*qbits);
           mpi_add (value_w, value_w, tmpval);
         }
@@ -1570,7 +1570,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits,
       if (mpi_get_nbits (prime_p) >= pbits-1
           && check_prime (prime_p, val_2, 64, NULL, NULL) )
         break; /* Yes, P is prime, continue with Step 15.  */
-      
+
       /* Step 13: counter = counter + 1, offset = offset + n + 1. */
       counter++;
 
@@ -1582,7 +1582,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits,
   /* Step 15:  Save p, q, counter and seed.  */
 /*   log_debug ("fips186-2 pbits p=%u q=%u counter=%d\n", */
 /*              mpi_get_nbits (prime_p), mpi_get_nbits (prime_q), counter); */
-/*   log_printhex("fips186-2 seed:", seed, seedlen);  */
+/*   log_printhex("fips186-2 seed:", seed, seedlen); */
 /*   log_mpidump ("fips186-2 prime p", prime_p); */
 /*   log_mpidump ("fips186-2 prime q", prime_q); */
   if (r_q)
@@ -1790,7 +1790,7 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits,
           if (ec)
             goto leave;
           if (value_j == value_n)
-            mpi_clear_highbit (tmpval, value_b+1); /* (V_n mod 2^b) */
+            mpi_clear_highbit (tmpval, value_b); /* (V_n mod 2^b) */
           mpi_lshift (tmpval, tmpval, value_j*qbits);
           mpi_add (value_w, value_w, tmpval);
         }
index 229fd1c..703a816 100644 (file)
@@ -1,3 +1,11 @@
+2008-11-28  Werner Koch  <wk@g10code.com>
+
+       * fips186-dsa.c: New.
+
+       * fipsdrv.c (print_mpi_line, print_data_line): New.
+       (run_dsa_pqg_gen): New.
+       (usage): Add mode dsa-pqg-gen.
+
 2008-11-25  Werner Koch  <wk@g10code.com>
 
        * pubkey.c (get_dsa_key_new): New.
index 38901f6..f9f3925 100644 (file)
@@ -19,7 +19,7 @@
 ## Process this file with automake to produce Makefile.in
 
 TESTS = version t-mpi-bit prime register ac ac-schemes ac-data basic \
-        mpitests tsexp keygen pubkey hmac keygrip
+        mpitests tsexp keygen pubkey hmac keygrip fips186-dsa
 
 
 # random.c uses fork() thus a test for W32 does not make any sense.
index 8b4079c..c1edb57 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env perl
 #
-# $Id: cavs_driver.pl 1383 2008-10-30 11:45:31Z smueller $
+# $Id: cavs_driver.pl 1395 2008-11-10 15:18:03Z smueller $
 #
 # CAVS test driver (based on the OpenSSL driver)
 # Written by: Stephan Müller <sm@atsec.com>
 #      ANSI931_AES128MCT
 #      ANSI931_AES128VST
 #
+# DSA
+#      PQGGen
+#      SigGen
+#      SigVer
+#
 # RC4 (atsec developed tests)
 #      RC4KeyBD
 #      RC4MCT
@@ -105,7 +110,6 @@ use MIME::Base64;
 # Contains the command line options
 my %opt;
 
-
 #################################################################
 ##### Central interface functions to the external ciphers #######
 #################################################################
@@ -143,7 +147,7 @@ my $rsa_sign;
 # $2: hash algo
 # $3: file holding the public RSA key in PEM format
 # $4: file holding the signature in binary form
-# return: 1 == verfied / 0 == not verified
+# return: 1 == verified / 0 == not verified
 my $rsa_verify;
 
 # generate a new private RSA key with the following properties:
@@ -171,6 +175,9 @@ my $hash;
 # $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
@@ -196,6 +203,40 @@ my $state_rng;
 # 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;
+
+# 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
 ################################################################
@@ -273,8 +314,6 @@ sub openssl_state_cipher($$$$$) {
        my $key = shift;
        my $iv = shift;
 
-        #FIXME: Implement the inner loop right here.
-
        my $enc = $encdec ? "-e": "-d";
 
        # We only invoke the driver with the IV parameter, if we have
@@ -362,7 +401,28 @@ sub libgcrypt_state_cipher($$$$$) {
        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;
 }
 
@@ -384,6 +444,13 @@ sub libgcrypt_hmac($$$$) {
        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);
+}
+
 ######### End of libgcrypt implementation ################
 
 ################################################################
@@ -532,39 +599,6 @@ sub fix_key_parity($) {
 }
 
 ####################################################
-# Encrypt/Decrypt routines
-
-# encryption
-# $1 key in hex form
-# $2 iv in hex form
-# $3 cipher
-# $4 data in hex form
-# return encrypted data
-sub encrypt($$$$) {
-       my $key=shift;
-       my $iv=shift;
-       my $cipher=shift;
-       my $data=shift;
-
-       return &$encdec($key, $iv, $cipher, 1, $data);
-}
-
-# decryption
-# $1 key in hex form
-# $2 iv in hex form
-# $3 cipher
-# $4 data in hex form
-# return encrypted data
-sub decrypt($$$$) {
-       my $key=shift;
-       my $iv=shift;
-       my $cipher=shift;
-       my $data=shift;
-
-       return &$encdec($key, $iv, $cipher, 0, $data);
-}
-
-####################################################
 # DER/PEM utility functions
 # Cf. http://www.columbia.edu/~ariel/ssleay/layman.html
 
@@ -920,10 +954,10 @@ sub kat($$$$$$$$) {
        $out .= "IV = $iv\n" if (defined($iv) && $iv ne "");
        if ($enc) {
                $out .= "PLAINTEXT = $pt\n";
-               $out .= "CIPHERTEXT = " . encrypt($key1, $iv, $cipher, $pt) . "\n";
+               $out .= "CIPHERTEXT = " . &$encdec($key1, $iv, $cipher, 1, $pt) . "\n";
        } else {
                $out .= "CIPHERTEXT = $pt\n";
-               $out .= "PLAINTEXT = " . decrypt($key1, $iv, $cipher, $pt) . "\n";
+               $out .= "PLAINTEXT = " . &$encdec($key1, $iv, $cipher, 0, $pt) . "\n";
        }
 
        return $out;
@@ -1008,8 +1042,6 @@ sub crypto_mct($$$$$$$$) {
         my $source_data = hex2bin(shift);
        my $cipher = shift;
         my $enc = shift;
-        my $line;
-        my $next_source;
 
        my $out = "";
 
@@ -1025,18 +1057,7 @@ sub crypto_mct($$$$$$$$) {
        my $iloop=1000;
        if ($ciph =~ /des/) {$oloop=400;$iloop=10000;}
 
-        my ($CO, $CI);
-        my $cipher_imp = &$state_cipher($cipher, $enc, $bufsize, $key1, $iv);
-        my $pid = open2($CO, $CI, $cipher_imp);
-        my $len;
-
         for (my $i=0; $i<$oloop; ++$i) {
-                my $calc_data;
-                my $old_calc_data;
-                my $old_old_calc_data;
-                my $ov;
-                my $iv_arg;
-
                $out .= "COUNT = $i\n";
                if (defined($key2)) {
                        $out .= "$keytype = ". bin2hex($key1). "\n";
@@ -1059,45 +1080,69 @@ sub crypto_mct($$$$$$$$) {
                 } else {
                         $out .= "CIPHERTEXT = ". bin2hex($source_data). "\n";
                 }
-
-                # Need to provide a dummy IV in case of ECB mode.
-                $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;
-                
-                # fixme: We should skip over empty lines here.
-
-                chomp($line = <$CO>); #print STDERR "        calc=$line\n";
-                $calc_data = hex2bin($line);
-
-                chomp($line = <$CO>); #print STDERR "    old_calc=$line\n";
-                $old_calc_data = hex2bin($line);
-
-                chomp($line = <$CO>); #print STDERR "old_old_calc=$line\n";
-                $old_old_calc_data = hex2bin($line);
-                
-                chomp($line = <$CO>); #print STDERR "          ov=$line\n";
-                $ov = hex2bin($line);
-                
-                chomp($line = <$CO>); #print STDERR " next source=$line\n";
-                $next_source = hex2bin($line);
-
-                # Skip over empty line.
-                $line = <$CO>;
-
+                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/) {
+                       print $CI "1\n"
+                                 .$iloop."\n"
+                                 .bin2hex($key1)."\n"
+                                 .bin2hex($iv)."\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);
+                       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";
@@ -1135,13 +1180,15 @@ sub crypto_mct($$$$$$$$) {
                        die "Test limitation: cipher '$cipher' not supported in Monte Carlo testing";
                }
 
-                if ($ciph =~ /des/) {
-                    $iv = $ov if (defined($iv) && $iv ne "");
-                    if ($cipher =~ /des-ede3-ofb/) {
+               if ($cipher =~ /des-ede3-ofb/) {
                         $source_data = $source_data ^ $next_source;
-                    } else {
-                        $source_data = $next_source;
-                    }
+               } elsif (!$enc && $cipher =~ /des-ede3-cfb/) {
+                       #TDES decryption CFB has a special rule
+                       $source_data = $next_source;
+               } elsif (! $enc && $ciph =~ /des/ ) {
+                       #TDES in decryption mode has a special rule
+                       $iv = $old_calc_data;
+                       $source_data = $calc_data;
                } elsif ( $ciph =~ /rc4/ || $cipher =~ /ecb/ ) {
                        #No resetting of IV as the IV is all zero set initially (i.e. no IV)
                        $source_data = $calc_data;
@@ -1149,11 +1196,7 @@ sub crypto_mct($$$$$$$$) {
                        $iv = $calc_data;
                        $source_data = $old_calc_data;
                }
-
         }
-        close $CO;
-        close $CI;
-        waitpid $pid, 0;
 
        return $out;
 }
@@ -1291,6 +1334,96 @@ sub rngx931($$$$) {
        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));
+               $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.$$";
+       gen_pubdsakey($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
 #
@@ -1298,12 +1431,16 @@ sub rngx931($$$$) {
 sub usage() {
 
        print STDERR "Usage:
-$0 [-R] [-I name] <CAVS-test vector file>
+$0 [-R] [-D] [-I name] <CAVS-test vector file>
 
 -R     execution of ARCFOUR instead of OpenSSL
 -I NAME        Use interface style NAME:
                openssl     OpenSSL (default)
-               libgcrypt   Libgcrypt";
+               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
@@ -1316,9 +1453,6 @@ sub parse($$) {
 
        my $out = "";
 
-       # Do I need to generate the key?
-       my $rsa_keygen = 0;
-
        # this is my cipher/hash type
        my $cipher = "";
 
@@ -1344,10 +1478,19 @@ sub parse($$) {
        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 $capital_s = "";
 
        my $mode = "";
 
@@ -1378,7 +1521,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)/) {
+               if ($tmpline =~ /^#.*(CBC|ECB|OFB|CFB|SHA-|SigGen|SigVer|RC4VS|ANSI X9\.31|Hash sizes tested|PQGGen)/) {
                        if ($tmpline    =~ /CBC/)   { $mode="cbc"; }
                        elsif ($tmpline =~ /ECB/)   { $mode="ecb"; }
                        elsif ($tmpline =~ /OFB/)   { $mode="ofb"; }
@@ -1397,10 +1540,6 @@ sub parse($$) {
                                $cipher="sha1"; #place holder - might be overwritten later
                        }
 
-                       # RSA Key Generation test
-                       if ($tmpline =~ /SigGen/) {
-                               $rsa_keygen = 1;
-                       }
                        if ($tmpline =~ /^#.*AESVS/) {
                                # AES cipher (part of it)
                                $cipher="aes";
@@ -1431,12 +1570,19 @@ sub parse($$) {
 
                        if ($tt == 0) {
                        ##### Identify the test type
-                               if ($tmpline =~ /KeyGen RSA \(X9.31\)/) {
-                                       $tt =~ 10;
-                                       die "Interface function for RSA KeyGen testing not defined for tested library"
-                                               if (!defined($gen_rsakey));
-                               }
-                               if ($tmpline =~ /Hash sizes tested/) {
+                               if ($tmpline =~ /SigVer/ && $opt{'D'} ) {
+                                       $tt = 12;
+                                       die "Interface function dsa_verify for dSA verification not defined for tested library"
+                                               if (!defined($dsa_verify));
+                               } 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));
@@ -1463,7 +1609,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));
+                                               if (!defined($state_cipher) || !defined($state_cipher_des));
                                } elsif ($cipher =~ /^sha/) {
                                        $tt = 3;
                                        die "Interface function hash for Hashing not defined for tested library"
@@ -1547,19 +1693,26 @@ sub parse($$) {
                        $pt=$2;
                }
                elsif ($line =~ /^\[mod\s*=\s*(.*)\]$/) { # found in RSA requests
-                       $out .= $line . "\n"; # print it
+                       $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) {
+                       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($1, $rsa_keyfile);
+                               &$gen_rsakey($modulus, $rsa_keyfile);
                                my $modulus = pipe_through_program("", "openssl rsa -pubout -modulus -in $rsa_keyfile");
                                $modulus =~ s/Modulus=(.*?)\s(.|\s)*/$1/;
-                               $out .= "\nn = $modulus\n";
-                               $out .= "\ne = 10001\n"
+                               $out .= "n = $modulus\n";
+                               $out .= "\ne = 10001\n"
                        }
                }
                elsif ($line =~ /^SHAAlg\s*=\s*(.*)/) { #found in RSA requests
@@ -1596,6 +1749,44 @@ sub parse($$) {
                                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 =~ /^S\s*=\s*(.)/) { #DSA SigVer
+                       die "S seen twice - check input file"
+                               if ($capital_s);
+                       $capital_s = $1;
+               }
                else {
                        $out .= $line . "\n";
                }
@@ -1674,6 +1865,48 @@ sub parse($$) {
                                $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 "" &&
+                           $capital_s ne "" &&
+                           $pt ne "") {
+                               $out .= dsa_sigver($modulus,
+                                                  $capital_p,
+                                                  $capital_q,
+                                                  $capital_g,
+                                                  $capital_y,
+                                                  $capital_r,
+                                                  $capital_s,
+                                                  $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 = "";
+                               $capital_s = "";
+                               $pt = "";
+                       }
+               }
                elsif ($tt > 0) {
                        die "Test case $tt not defined";
                }
@@ -1704,7 +1937,7 @@ sub main() {
 
        usage() unless @ARGV;
 
-       getopts("RI:", \%opt) or die "bad option";
+       getopts("DRI:", \%opt) or die "bad option";
 
        ##### Set library
 
@@ -1724,8 +1957,10 @@ sub main() {
                $gen_rsakey =   \&libgcrypt_gen_rsakey;
                $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;
         } else {
                 die "Invalid interface option given";
         }
diff --git a/tests/fips186-dsa.c b/tests/fips186-dsa.c
new file mode 100644 (file)
index 0000000..c676c5d
--- /dev/null
@@ -0,0 +1,460 @@
+/* fips186-dsa.c - FIPS 186 DSA 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "../src/gcrypt.h"
+
+#define my_isascii(c) (!((c) & 0x80))
+#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a)                     \
+                      || (*(a) >= 'A' && *(a) <= 'F')  \
+                      || (*(a) >= 'a' && *(a) <= 'f'))
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+#define DIM(v)              (sizeof(v)/sizeof((v)[0]))
+#define DIMof(type,member)   DIM(((type *)0)->member)
+
+static int verbose;
+static int error_count;
+
+static void
+info (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+}
+
+static void
+fail (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  error_count++;
+}
+
+static void
+die (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  exit (1);
+}
+
+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);
+}
+
+static gcry_mpi_t
+mpi_from_string (const char *string)
+{
+  gpg_error_t err;
+  gcry_mpi_t a;
+
+  err = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, string, 0, NULL);
+  if (err)
+    die ("error converting string to mpi: %s\n", gpg_strerror (err));
+  return a;
+}
+
+/* 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 *
+data_from_hex (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))
+        die ("error parsing hex string `%s'\n", string);
+      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
+    }
+  *r_length = length;
+  return buffer;
+}
+
+
+static void
+extract_cmp_mpi (gcry_sexp_t sexp, const char *name, const char *expected)
+{
+  gcry_sexp_t l1;
+  gcry_mpi_t a, b;
+
+  l1 = gcry_sexp_find_token (sexp, name, 0);
+  a = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG);
+  b = mpi_from_string (expected);
+  if (!a) 
+    fail ("parameter \"%s\" missing in key\n", name);
+  else if ( gcry_mpi_cmp (a, b) )
+    fail ("parameter \"%s\" does not match expected value\n", name);
+  gcry_mpi_release (b);
+  gcry_mpi_release (a);
+  gcry_sexp_release (l1);
+}
+
+
+static void
+extract_cmp_data (gcry_sexp_t sexp, const char *name, const char *expected)
+{
+  gcry_sexp_t l1;
+  const void *a;
+  size_t alen;
+  void *b;
+  size_t blen;
+
+  l1 = gcry_sexp_find_token (sexp, name, 0);
+  a = gcry_sexp_nth_data (l1, 1, &alen);
+  b = data_from_hex (expected, &blen);
+  if (!a) 
+    fail ("parameter \"%s\" missing in key\n", name);
+  else if ( alen != blen || memcmp (a, b, alen) )
+    fail ("parameter \"%s\" does not match expected value\n", name);
+  gcry_free (b);
+  gcry_sexp_release (l1);
+}
+
+static void
+extract_cmp_int (gcry_sexp_t sexp, const char *name, int expected)
+{
+  gcry_sexp_t l1;
+  char *a;
+
+  l1 = gcry_sexp_find_token (sexp, name, 0);
+  a = gcry_sexp_nth_string (l1, 1);
+  if (!a) 
+    fail ("parameter \"%s\" missing in key\n", name);
+  else if ( strtoul (a, NULL, 10) != expected )
+    fail ("parameter \"%s\" does not match expected value\n", name);
+  gcry_free (a);
+  gcry_sexp_release (l1);
+}
+
+
+static void
+check_dsa_gen_186_2 (void)
+{
+  static struct {
+    int nbits;
+    const char *p, *q, *g;
+    const char *seed;
+    int counter;
+    const char *h;
+  } tbl[] = {
+    /* These tests are from FIPS 186-2, B.3.1.  */
+    {
+      1024,
+      "d3aed1876054db831d0c1348fbb1ada72507e5fbf9a62cbd47a63aeb7859d6921"
+      "4adeb9146a6ec3f43520f0fd8e3125dd8bbc5d87405d1ac5f82073cd762a3f8d7"
+      "74322657c9da88a7d2f0e1a9ceb84a39cb40876179e6a76e400498de4bb9379b0"
+      "5f5feb7b91eb8fea97ee17a955a0a8a37587a272c4719d6feb6b54ba4ab69",
+      "9c916d121de9a03f71fb21bc2e1c0d116f065a4f",
+      "8157c5f68ca40b3ded11c353327ab9b8af3e186dd2e8dade98761a0996dda99ab"
+      "0250d3409063ad99efae48b10c6ab2bba3ea9a67b12b911a372a2bba260176fad"
+      "b4b93247d9712aad13aa70216c55da9858f7a298deb670a403eb1e7c91b847f1e"
+      "ccfbd14bd806fd42cf45dbb69cd6d6b43add2a78f7d16928eaa04458dea44",
+      "0cb1990c1fd3626055d7a0096f8fa99807399871",
+      98,
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000000000000000000000000000000000000000000000002"
+    },
+    {
+      1024,
+      "f5c73304080353357de1b5967597c27d65f70aa2fe9b6aed1d0afc2b499adf22f"
+      "8e37937096d88548ac36c4a067f8353c7fed73f96f0d688b19b0624aedbae5dbb"
+      "0ee8835a4c269288c0e1d69479e701ee266bb767af39d748fe7d6afc73fdf44be"
+      "3eb6e661e599670061203e75fc8b3dbd59e40b54f358d0097013a0f3867f9",
+      "f8751166cf4f6f3b07c081fd2a9071f23ca1988d",
+      "1e288a442e02461c418ed67a66d24cacbeb8936fbde62ff995f5fd569dee6be62"
+      "4e4f0f9f8c8093f5d192ab3b3f9ae3f2665d95d27fb10e382f45cd356e7f4eb7a"
+      "665db432113ed06478f93b7cf188ec7a1ee97aec8f91ea7bfceaf8b6e7e5a349c"
+      "4ad3225362ef440c57cbc6e69df15b6699caac85f733555075f04781b2b33",
+      "34b3520d45d240a8861b82c8b61ffa16e67b5cce",
+      622,
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000000000000000000000000000000000000000000000002",
+    },
+    {
+      1024,
+      "c6c6f4f4eed927fb1c3b0c81010967e530658e6f9698ebe058b4f47b2dc8fcbc7"
+      "b69296b9e8b6cf55681181fe72492668061b262b0046a0d409902e269b0cb69a4"
+      "55ed1a086caf41927f5912bf0e0cbc45ee81a4f98bf6146f6168a228aec80e9cc"
+      "1162d6f6aa412efe82d4f18b95e34ab790daac5bd7aef0b22fa08ba5dbaad",
+      "d32b29f065c1394a30490b6fcbf812a32a8634ab",
+      "06f973c879e2e89345d0ac04f9c34ad69b9eff1680f18d1c8f3e1596c2e8fa8e1"
+      "ecef6830409e9012d4788bef6ec7414d09c981b47c941b77f39dfc49caff5e714"
+      "c97abe25a7a8b5d1fe88700bb96eff91cca64d53700a28b1146d81bad1212d231"
+      "80154c95a01f5aeebb553a8365c38a5ebe05539b51734233776ce9aff98b2",
+      "b6ec750da2f824cb42c5f7e28c81350d97f75125",
+      185,
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000000000000000000000000000000000000000000000002",
+    },
+    {
+      1024,
+      "b827a9dc9221a6ed1bec7b64d61232aacb2812f888b0a0b3a95033d7a22e77d0b"
+      "ff23bfeed0fb1281b21b8ff7421f0c727d1fb8aa2b843d6885f067e763f83d41f"
+      "d800ab15a7e2b12f71ec2058ee7bd62cd72c26989b272e519785da57bfa1f974b"
+      "c652e1a2d6cfb68477de5635fd019b37add656cff0b802558b31b6d2851e5",
+      "de822c03445b77cec4ad3a6fb0ca39ff97059ddf",
+      "65a9e2d43a378d7063813104586868cacf2fccd51aec1e0b6af8ba3e66dee6371"
+      "681254c3fb5e3929d65e3c4bcd20abd4ddc7cf815623e17b9fc92f02b8d44278b"
+      "848480ffd193104cf5612639511e45bd247708ff6028bd3824f8844c263b46c69"
+      "1f2076f8cd13c5d0be95f1f2a1a17ab1f7e5bc73500bac27d57b473ba9748",
+      "cd2221dd73815a75224e9fde7faf52829b81ac7a",
+      62,
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000000000000000000000000000000000000000000000002",
+    },
+    {
+      1024,
+      "898a8d93e295c8ef2ffd46976225a1543640640d155a576fafa0be32136165803"
+      "ba2eff2782a2be75cc9ec65db6bd3238cca695b3a5a14726a2a314775c377d891"
+      "354b3de6c89e714a05599ca04132c987f889f72c4fe298ccb31f711c03b07e1d9"
+      "8d72af590754cf3847398b60cecd55a4611692b308809560a83880404c227",
+      "c6d786643d2acfc6b8d576863fda8cfbfbd5e03f",
+      "2fd38b8d21c58e8fb5315a177b8d5dc4c450d574e69348b7b9da367c26e72438d"
+      "af8372e7f0bee84ef5dcbbc3727194a2228431192f1779be24837f22a0e14d10d"
+      "5344da1b8b403df9f9b2655095b3d0f67418ed6cd989f35aa4232e4b7001764fb"
+      "e85d6b2c716980f13272fc4271ac1e234f7e24c023cfc2d2dc0aa1e9af2fb",
+      "73483e697599871af983a281e3afa22e0ed86b68",
+      272,
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "00000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000000000000000000000000000000000000000000000002",
+    },
+
+    /* These tests are generated by the OpenSSL FIPS version.  */
+    { 
+      1024,
+      "A404363903FDCE86839BCFD953AAD2DA2B0E70CAED3B5FF5D68F15A1C4BB0A793C"
+      "A9D58FC956804C5901DE0AF99F345ED1A8617C687864BAC044B7C3C3E732A2B255"
+      "EC986AA76EA8CB0E0815B3E0E605650AF7D8058EE7E8EBCDEFFDAB8100D3FC1033"
+      "11BA3AB232EF06BB74BA9A949EC0C7ED324C19B202F4AB725BBB4080C9",
+      "C643946CEA8748E12D430C48DB038F9165814389",
+      "59B7E7BA0033CCE8E6837173420FBB382A784D4154A3C166043F5A68CB92945D16"
+      "892D4CC5585F2D28C780E75A6C20A379E2B58304C1E5FC0D8C15E4E89C4498C8BC"
+      "B90FB36ED8DC0489B9D0BC09EC4411FB0BFADF25485EEAB6700BE0ACF5C44A6ED7"
+      "44A015382FF9B8DA7EAA00DEA135FADC59212DBBFFC1537336FA4B7225",
+      "02708ab36e3f0bfd67ec3b8bd8829d03b84f56bd",
+      50,
+      "02"
+    },
+    {
+      1024,
+      "9C664033DB8B203D826F896D2293C62EF9351D5CFD0F4C0AD7EFDA4DDC7F15987"
+      "6A3C68CAB2586B44FD1BD4DEF7A17905D88D321DD77C4E1720D848CA21D79F9B3"
+      "D8F537338E09B44E9F481E8DA3C56569F63146596A050EF8FAEE8ACA32C666450"
+      "04F675C8806EB4025B0A5ECC39CE89983EA40A183A7CF5208BA958045ABD5",
+      "AD0D8CBA369AF6CD0D2BAC0B4CFCAF0A1F9BCDF7",
+      "74D717F7092A2AF725FDD6C2561D1DBE5AEE40203C638BA8B9F49003857873701"
+      "95A44E515C4E8B344F5CDC7F4A6D38097CD57675E7643AB9700692C69F0A99B0E"
+      "039FDDDFCA8CEB607BDB4ADF2834DE1690F5823FC8199FB8F6F29E5A583B6786A"
+      "C14C7E67106C3B30568CBB9383F89287D578159778EB18216799D16D46498",
+      "6481a12a50384888ee84b61024f7c9c685d6ac96",
+      289,
+      "02"
+    },
+    {
+      1024,
+
+      "B0DFB602EB8462B1DC8C2214A52B587D3E6842CCF1C38D0F7C7F967ED30CF6828"
+      "1E2675B3BAB594755FB1634E66B4C23936F0725A358F8DFF3C307E2601FD66D63"
+      "5B17270450C50BD2BEC29E0E9A471DF1C15B0191517952268A2763D4BD28B8503"
+      "B3399686272B76B11227F693D7833105EF70C2289C3194CF4527024B272DF",
+      "EA649C04911FAB5A41440287A517EF752A40354B",
+      "88C5A4563ECB949763E0B696CD04B21321360F54C0EE7B23E2CEDC30E9E486162"
+      "01BFB1619E7C54B653D1F890C50E04B29205F5E3E2F93A13B0751AF25491C5194"
+      "93C09DDF6B9C173B3846DFB0E7A5C870BBFC78419260C90E20315410691C8326C"
+      "858D7063E7921F3F601158E912C7EE487FF259202BEEB10F6D9E99190F696",
+      "5bf9d17bc62fbbf3d569c92bd4505586b2e5ef1a",
+      626,
+      "02"
+    },
+    {
+      1024,
+      "F783C08D7F9463E48BA87893805C4B34B63C85DF7EBDD9EBEE94DB4AF4E4A415C"
+      "F0F3793AE55096BA1199598798FA8403B28DED7F7C7AFD54FD535861A0150EF4D"
+      "5871465B13837CCF46BEB0A22F8D38DC7D6AE0E14A3845FD0C027CFA97791B977"
+      "CE2808BAD9B43CE69390C0F40016056722D82C0D7B1B27413D026A39D7DAD",
+      "A40D9EE456AED4C8A653FDB47B6629C0B843FE8F",
+      "DF876263E21F263AE6DA57409BD517DCEADB9216048F066D6B58867F8E59A5EEE"
+      "700283A946C1455534618979BE6C227673C1B803910262BD93BC94D5089850614"
+      "F3E29AB64E8C989A7E3E28FE670FFA3EE21DEEEC1AB0B60E1D8E2AA39663BADD7"
+      "2C9F957D7F3D4F17D9FDAD050EB373A6DEFD09F5DA752EAFE046836E14B67",
+      "8a9a57706f69f4f566252cdf6d5cbfdf2020150b",
+      397,
+      "02"
+    },
+    { 
+      1024,
+      "D40E4F6461E145859CCF60FD57962840BD75FFF12C22F76626F566842252AD068"
+      "29745F0147056354F6C016CF12762B0E331787925B8128CF5AF81F9B176A51934"
+      "96D792430FF83C7B79BD595BDA10787B34600787FA552EFE3662F37B99AAD3F3A"
+      "093732680A01345192A19BECCE6BF5D498E44ED6BED5B0BA72AAD49E8276B",
+      "D12F1BD0AA78B99247FD9F18EAFEE5C136686EA5",
+      "468EBD20C99449C1E440E6F8E452C6A6BC7551C555FE5E94996E20CFD4DA3B9CC"
+      "58499D6CC2374CCF9C392715A537DE10CFCA8A6A37AFBD187CF6B88D26881E5F5"
+      "7521D9D2C9BBA51E7B87B070BBE73F5C5FE31E752CAF88183516D8503BAAC1159"
+      "928EF50DEE52D96F396B93FB4138D786464C315401A853E57C9A0F9D25839",
+      "30b3599944a914a330a3f49d11ec88f555422aef",
+      678,
+      "02"
+    }
+  };
+  gpg_error_t err;
+  int tno;
+  gcry_sexp_t key_spec, key, pub_key, sec_key, seed_values;
+  gcry_sexp_t l1;
+
+  for (tno = 0; tno < DIM (tbl); tno++)
+    {
+      if (verbose)
+        info ("generating FIPS 186-2 test key %d\n", tno);
+
+      {
+        void *data;
+        size_t datalen;
+
+        data = data_from_hex (tbl[tno].seed, &datalen);
+        err = gcry_sexp_build (&key_spec, NULL,
+                               "(genkey (dsa (nbits %d)(use-fips186-2)"
+                               "(derive-parms(seed %b))))",
+                               tbl[tno].nbits, (int)datalen, data);
+        gcry_free (data);
+      }
+      if (err)
+        die ("error creating S-expression %d: %s\n", tno, gpg_strerror (err));
+
+      err = gcry_pk_genkey (&key, key_spec);
+      gcry_sexp_release (key_spec);
+      if (err)
+        {
+          fail ("error generating key %d: %s\n", tno, gpg_strerror (err));
+          continue;
+        }
+      
+      if (verbose > 1)
+        show_sexp ("generated key:\n", key);
+
+      pub_key = gcry_sexp_find_token (key, "public-key", 0);
+      if (!pub_key)
+        fail ("public part missing in key %d\n", tno);
+
+      sec_key = gcry_sexp_find_token (key, "private-key", 0);
+      if (!sec_key)
+        fail ("private part missing in key %d\n", tno);
+
+      l1 = gcry_sexp_find_token (key, "misc-key-info", 0);
+      if (!l1)
+        fail ("misc_key_info part missing in key %d\n", tno);
+      seed_values = gcry_sexp_find_token (l1, "seed-values", 0);
+      if (!seed_values)
+        fail ("seed-values part missing in key %d\n", tno);
+      gcry_sexp_release (l1);
+
+      extract_cmp_mpi (sec_key, "p", tbl[tno].p);
+      extract_cmp_mpi (sec_key, "q", tbl[tno].q);
+      extract_cmp_mpi (sec_key, "g", tbl[tno].g);
+
+      extract_cmp_data (seed_values, "seed", tbl[tno].seed);
+      extract_cmp_int (seed_values, "counter", tbl[tno].counter);
+      extract_cmp_mpi (seed_values, "h", tbl[tno].h);
+
+      gcry_sexp_release (seed_values);
+      gcry_sexp_release (sec_key);
+      gcry_sexp_release (pub_key);
+      gcry_sexp_release (key);
+    }
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+  int debug = 0;
+
+  if (argc > 1 && !strcmp (argv[1], "--verbose"))
+    verbose = 1;
+  else if (argc > 1 && !strcmp (argv[1], "--debug"))
+    {
+      verbose = 2; 
+      debug = 1;
+    }
+
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+  if (!gcry_check_version (GCRYPT_VERSION))
+    die ("version mismatch\n");
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+  if (debug)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
+  /* No valuable keys are create, so we can speed up our RNG. */
+  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
+
+
+  check_dsa_gen_186_2 ();
+
+
+  return error_count ? 1 : 0;
+}
index e27b84c..d20b2bb 100644 (file)
@@ -823,6 +823,53 @@ print_buffer (const void *buffer, size_t length)
 }
 
 
+/* 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));
+}
+
+
+
 
 static gcry_error_t
 init_external_rng_test (void **r_context, 
@@ -1487,6 +1534,105 @@ run_rsa_verify (const void *data, size_t datalen, int hashalgo, int pkcs1,
 }
 
 
+\f
+/* Generate DSA donmain 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.  */
+static void
+run_dsa_pqg_gen (int keysize)
+{
+  gpg_error_t err;
+  gcry_sexp_t keyspec, key, l1, l2;
+  gcry_mpi_t mpi;
+  int idx;
+  const void *data;
+  size_t datalen;
+  char *string;
+
+  /* Note that we create a complete key but don't return the x and y
+     values.  */
+  err = gcry_sexp_build (&keyspec, NULL, 
+                         "(genkey (dsa (nbits %d)(use-fips186-2)))",
+                         keysize);
+  if (err)
+    die ("gcry_sexp_build failed for DSA domain parameter 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");
+
+  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; "pqg"[idx]; idx++) 
+    {
+      l2 = gcry_sexp_find_token (l1, "pqg"+idx, 1);
+      if (!l2)
+        die ("no %c parameter in returned private key\n", "pqg"[idx]);
+      mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+      if (!mpi)
+        die ("no value for %c parameter in returned private key\n","pqg"[idx]);
+      gcry_sexp_release (l2);
+      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 private key\n");
+  gcry_sexp_release (l1);
+  l1 = l2;
+
+  l2 = gcry_sexp_find_token (l1, "seed", 0);
+  if (!l2)
+    die ("no seed value in returned private key\n");
+  data = gcry_sexp_nth_data (l2, 1, &datalen);
+  if (!data)
+    die ("no seed value in returned private key\n");
+  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 private key\n");
+  string = gcry_sexp_nth_string (l2, 1);
+  if (!string)
+    die ("no counter value in returned private key\n");
+  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 private key\n");
+  mpi = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+  if (!mpi)
+    die ("no h value in returned private key\n");
+  print_mpi_line (mpi, 1);
+  gcry_mpi_release (mpi);
+  gcry_sexp_release (l2);
+
+  gcry_sexp_release (l1);
+  gcry_sexp_release (key);
+}
+
 
 \f
 static void
@@ -1502,7 +1648,8 @@ usage (int show_help)
     ("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, rsa-{gen,sign,verify}\n"
+     "  encrypt, decrypt, digest, random, hmac-sha, rsa-{gen,sign,verify},\n"
+     "  dsa-pqg-gen\n"
      "OPTIONS:\n"
      "  --verbose        Print additional information\n"
      "  --binary         Input and output is in binary form\n"
@@ -1695,7 +1842,8 @@ main (int argc, char **argv)
   if (!chunksize
       && !mct_server
       && strcmp (mode_string, "random")
-      && strcmp (mode_string, "rsa-gen") )
+      && strcmp (mode_string, "rsa-gen")
+      && strcmp (mode_string, "dsa-pqg-gen") )
     {
       data = read_file (input, !binary_input, &datalen);
       if (!data)
@@ -1933,6 +2081,15 @@ main (int argc, char **argv)
                       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);
+    }
   else
     usage (0);