More DSA FIPS test suport.
authorWerner Koch <wk@gnupg.org>
Wed, 3 Dec 2008 15:14:48 +0000 (15:14 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 3 Dec 2008 15:14:48 +0000 (15:14 +0000)
tests/ChangeLog
tests/cavs_driver.pl
tests/fipsdrv.c

index 40510e8..5b8f7df 100644 (file)
@@ -1,3 +1,11 @@
+2008-12-03  Werner Koch  <wk@g10code.com>
+
+       * fipsdrv.c (run_dsa_pqg_gen): Facor code out into ..
+       (print_dsa_domain_parameters, dsa_gen): .. these two new functions.
+       (print_sexp, read_sexp_from_file): New.
+       (run_dsa_sign): New.
+       (run_dsa_verify): New.
+
 2008-12-02  Werner Koch  <wk@g10code.com>
 
        * fipsdrv.c: All standalone build.
index c1edb57..739cd00 100755 (executable)
@@ -217,6 +217,16 @@ my $hmac;
 #         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
@@ -451,6 +461,75 @@ sub libgcrypt_dsa_pqggen($) {
        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 --verbose --key $keyfile --signature $sigfile dsa-verify");
+        unlink ($sigfile);
+       # Parse through the output information
+       return ($ret =~ /GOOD signature/);
+}
+
+
+
 ######### End of libgcrypt implementation ################
 
 ################################################################
@@ -1415,7 +1494,7 @@ sub dsa_sigver($$$$$$$$) {
        # 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);
+       &dsa_genpubkey($keyfile, $p, $q, $g, $y);
 
        $out .= "Result = " . (&$dsa_verify($msg, $keyfile, $r, $s) ? "P\n" : "F\n");
 
@@ -1572,8 +1651,8 @@ sub parse($$) {
                        ##### Identify the test type
                                if ($tmpline =~ /SigVer/ && $opt{'D'} ) {
                                        $tt = 12;
-                                       die "Interface function dsa_verify for dSA verification not defined for tested library"
-                                               if (!defined($dsa_verify));
+                                       die "Interface function dsa_verify or dsa_genpubey 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"
@@ -1927,6 +2006,8 @@ sub cleanup() {
        unlink("rsa_sigver.tmp.$$.sig");
        unlink("rsa_sigver.tmp.$$.der");
        unlink("rsa_sigver.tmp.$$.cnf");
+
+       unlink("dsa_sigver.tmp.$$.sig");
        exit;
 }
 
@@ -1961,6 +2042,10 @@ sub main() {
                $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";
         }
index 7dedf12..31e624d 100644 (file)
@@ -739,6 +739,34 @@ read_sig_file (const char *fname)
 }
 
 
+/* 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)
 {
@@ -875,6 +903,21 @@ print_data_line (const void *data, size_t datalen)
     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);
+}
+
 
 
 
@@ -1542,42 +1585,52 @@ 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)
+/* 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, l1, l2;
-  gcry_mpi_t mpi;
-  int idx;
-  const void *data;
-  size_t datalen;
-  char *string;
+  gcry_sexp_t keyspec, key;
 
-  /* 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",
+    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 RSA: %s\n", gpg_strerror (err));
+    die ("gcry_pk_genkey failed for DSA: %s\n", gpg_strerror (err));
   
   gcry_sexp_release (keyspec);
 
-  l1 = gcry_sexp_find_token (key, "private-key", 0);
+  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 ("private key not found in genkey result\n");
+    die ("public 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");
+    die ("returned public key not formed as expected\n");
   gcry_sexp_release (l1);
   l1 = l2;
 
@@ -1586,10 +1639,10 @@ run_dsa_pqg_gen (int keysize)
     {
       l2 = gcry_sexp_find_token (l1, "pqg"+idx, 1);
       if (!l2)
-        die ("no %c parameter in returned private key\n", "pqg"[idx]);
+        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 private key\n","pqg"[idx]);
+        die ("no value for %c parameter in returned public key\n","pqg"[idx]);
       gcry_sexp_release (l2);
       print_mpi_line (mpi, 1);
       gcry_mpi_release (mpi);
@@ -1603,45 +1656,214 @@ run_dsa_pqg_gen (int keysize)
 
   l2 = gcry_sexp_find_token (l1, "seed-values", 0);
   if (!l2)
-    die ("no seed-values in returned private key\n");
+    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 private key\n");
+    die ("no seed value in returned key\n");
   data = gcry_sexp_nth_data (l2, 1, &datalen);
   if (!data)
-    die ("no seed value in returned private key\n");
+    die ("no seed value in returned 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");
+    die ("no counter value in returned key\n");
   string = gcry_sexp_nth_string (l2, 1);
   if (!string)
-    die ("no counter value in returned private key\n");
+    die ("no counter value in returned 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");
+    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 private key\n");
+    die ("no h value in returned key\n");
   print_mpi_line (mpi, 1);
   gcry_mpi_release (mpi);
   gcry_sexp_release (l2);
 
   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.  */
+static void
+run_dsa_pqg_gen (int keysize)
+{
+  gcry_sexp_t key;
+
+  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);
 }
 
 
 \f
+/* 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;
+  gcry_mpi_t tmpmpi;
+
+  err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, data, datalen, 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);
+}
+
+
+\f
+/* 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;
+  gcry_mpi_t tmpmpi;
+  
+  err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, data, datalen, 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);
+}
+
+
+
+\f
 static void
 usage (int show_help)
 {
@@ -1656,7 +1878,7 @@ usage (int show_help)
      "Run a crypto operation using hex encoded input and output.\n"
      "MODE:\n"
      "  encrypt, decrypt, digest, random, hmac-sha, rsa-{gen,sign,verify},\n"
-     "  dsa-pqg-gen\n"
+     "  dsa-{pqg-gen,gen,sign,verify}\n"
      "OPTIONS:\n"
      "  --verbose        Print additional information\n"
      "  --binary         Input and output is in binary form\n"
@@ -1850,7 +2072,8 @@ main (int argc, char **argv)
       && !mct_server
       && strcmp (mode_string, "random")
       && strcmp (mode_string, "rsa-gen")
-      && strcmp (mode_string, "dsa-pqg-gen") )
+      && strcmp (mode_string, "dsa-pqg-gen")
+      && strcmp (mode_string, "dsa-gen") )
     {
       data = read_file (input, !binary_input, &datalen);
       if (!data)
@@ -2097,6 +2320,43 @@ main (int argc, char **argv)
         die ("invalid keysize specified; needs to be 1024 .. 3072\n");
       run_dsa_pqg_gen (keysize);
     }
+  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
     usage (0);