Implement mct server mode for fips driver and use it in the CAVS tests to
authorWerner Koch <wk@gnupg.org>
Fri, 7 Nov 2008 16:07:02 +0000 (16:07 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 7 Nov 2008 16:07:02 +0000 (16:07 +0000)
speed up things and to get hold of intermediate values we could not easily
retrieve with the old implementaion.

tests/ChangeLog
tests/cavs_driver.pl
tests/fipsdrv.c

index c438778..74a21a0 100644 (file)
@@ -1,3 +1,10 @@
+2008-11-07  Werner Koch  <wk@g10code.com>
+
+       * fipsdrv.c (run_cipher_mct_loop, get_current_iv): New.
+       (read_textline, read_hexline, skip_to_empty_line): New.
+       (main): New option --mct-server.
+       * cavs_driver.pl: Update from upstream and adjust to new fipsdrv.
+
 2008-11-05  Werner Koch  <wk@g10code.com>
 
        * fipsdrv.c (run_encrypt_decrypt): Disable weak key detection.
index 4cb9474..b6542fd 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env perl
 #
-# $Id: cavs_driver.pl 1243 2008-09-18 18:42:57Z smueller $
+# $Id: cavs_driver.pl 1383 2008-10-30 11:45:31Z smueller $
 #
 # CAVS test driver (based on the OpenSSL driver)
 # Written by: Stephan Müller <sm@atsec.com>
@@ -206,8 +206,12 @@ sub openssl_encdec($$$$$) {
        my $enc = (shift) ? "-e" : "-d";
        my $data=shift;
 
+       # We only invoke the driver with the IV parameter, if we have
+       # an IV, otherwise, we skip it
+       $iv = "-iv $iv" if ($iv);
+
        $data=hex2bin($data);
-       my $program="openssl enc -$cipher -nopad -nosalt -K $key $enc -iv $iv";
+       my $program="openssl enc -$cipher -nopad -nosalt -K $key $enc $iv";
        $program = "rc4 -k $key" if $opt{'R'}; #for ARCFOUR, no IV must be given
        $data=pipe_through_program($data,$program);
        return bin2hex($data);
@@ -269,9 +273,15 @@ sub openssl_state_cipher($$$$$) {
        my $key = shift;
        my $iv = shift;
 
+        #FIXME: Implement the inner loop right here.
+
        my $enc = $encdec ? "-e": "-d";
 
-       my $out = "openssl enc -'$cipher' $enc -nopad -nosalt -bufsize $bufsize -K ".bin2hex($key)." -iv ".bin2hex($iv);
+       # We only invoke the driver with the IV parameter, if we have
+       # an IV, otherwise, we skip it
+       $iv = "-iv ".bin2hex($iv) if ($iv);
+
+       my $out = "openssl enc -'$cipher' $enc -nopad -nosalt -bufsize $bufsize -K ".bin2hex($key)." $iv";
        #for ARCFOUR, no IV must be given
        $out = "rc4 -k " . bin2hex($key) if $opt{'R'};
        return $out;
@@ -289,11 +299,14 @@ sub libgcrypt_encdec($$$$$) {
        my $enc = (shift) ? "encrypt" : "decrypt";
        my $data=shift;
 
-        $iv = "--iv $iv" if ($iv);
+       # We only invoke the driver with the IV parameter, if we have
+       # an IV, otherwise, we skip it
+       $iv = "--iv $iv" if ($iv);
 
        my $program="fipsdrv --key $key $iv --algo $cipher $enc";
 
        return pipe_through_program($data,$program);
+
 }
 
 sub libgcrypt_rsa_sign($$$) {
@@ -302,8 +315,9 @@ sub libgcrypt_rsa_sign($$$) {
        my $keyfile = shift;
 
        die "ARCFOUR not available for RSA" if $opt{'R'};
+
        return pipe_through_program($data,
-               "fipsdrv --verbose --pkcs1 --algo $hashalgo --key $keyfile rsa-sign");
+               "fipsdrv --pkcs1 --algo $hashalgo --key $keyfile rsa-sign");
 }
 
 sub libgcrypt_rsa_verify($$$$) {
@@ -314,7 +328,7 @@ sub libgcrypt_rsa_verify($$$$) {
 
        die "ARCFOUR not available for RSA" if $opt{'R'};
        $data = pipe_through_program($data,
-               "fipsdrv --verbose --pkcs1 --algo $hashalgo --key $keyfile --signature $sigfile rsa-verify");
+               "fipsdrv --pkcs1 --algo $hashalgo --key $keyfile --signature $sigfile rsa-verify");
 
        # Parse through the output information
        return ($data =~ /GOOD signature/);
@@ -348,9 +362,7 @@ sub libgcrypt_state_cipher($$$$$) {
        my $key = shift;
        my $iv = shift;
 
-        $iv = "--iv $iv" if ($iv);
-
-       my $program="fipsdrv --binary --key ".bin2hex($key)." $iv ".bin2hex($iv)." --algo '$cipher' --chunk '$bufsize' $enc";
+       my $program="fipsdrv --algo '$cipher' --mct-server $enc";
        return $program;
 }
 
@@ -359,7 +371,7 @@ sub libgcrypt_state_rng($$$) {
        my $dt = shift;
        my $v = shift;
 
-       return "fipsdrv --binary --progress --loop --key $key --iv $v --dt $dt random";
+       return "fipsdrv --binary --loop --key $key --iv $v --dt $dt random";
 }
 
 sub libgcrypt_hmac($$$$) {
@@ -930,7 +942,9 @@ sub hash_kat($$$) {
        my $out = "";
        $out .= "Len = $len\n" if (defined($len));
        $out .= "Msg = $pt\n";
-       $out .= "MD = " . &$hash($pt, $cipher);
+
+       $pt = "" if(!$len);
+       $out .= "MD = " . &$hash($pt, $cipher) . "\n";
        return $out;
 }
 
@@ -994,6 +1008,8 @@ sub crypto_mct($$$$$$$$) {
         my $source_data = hex2bin(shift);
        my $cipher = shift;
         my $enc = shift;
+        my $line;
+        my $next_source;
 
        my $out = "";
 
@@ -1009,7 +1025,17 @@ 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;
+
                $out .= "COUNT = $i\n";
                if (defined($key2)) {
                        $out .= "$keytype = ". bin2hex($key1). "\n";
@@ -1032,43 +1058,40 @@ sub crypto_mct($$$$$$$$) {
                 } else {
                         $out .= "CIPHERTEXT = ". bin2hex($source_data). "\n";
                 }
-                my ($CO, $CI);
-               my $cipher_imp = &$state_cipher($cipher, $enc, $bufsize, $key1, $iv);
-                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]
-                for (my $j = 0; $j < $iloop; ++$j) {
-                       $old_old_calc_data = $old_calc_data;
-                        $old_calc_data = $calc_data;
-
-                       # $calc_data = AES($key, $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/ ) {
-                               #TDES in decryption mode and RC4 have a special rule
-                               $source_data = $calc_data;
-                       } else {
-                               $source_data = $old_calc_data;
-                       }
-                }
-                close $CO;
-                close $CI;
-                waitpid $pid, 0;
+
+                print $CI "1\n"
+                          .$iloop."\n"
+                          .bin2hex($key1)."\n"
+                          .bin2hex($iv)."\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>;
+
 
                 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";
@@ -1106,18 +1129,25 @@ sub crypto_mct($$$$$$$$) {
                        die "Test limitation: cipher '$cipher' not supported in Monte Carlo testing";
                }
 
-               if (! $enc && $ciph =~ /des/ ) {
-                       #TDES in decryption mode has a special rule
-                       $iv = $old_calc_data;
-                       $source_data = $calc_data;
-               } elsif ( $ciph =~ /rc4/ ) {
+                if ($ciph =~ /des/) {
+                    $iv = $ov;
+                    if ($cipher =~ /des-ede3-ofb/) {
+                        $source_data = $source_data ^ $next_source;
+                    } else {
+                        $source_data = $next_source;
+                    }
+               } 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;
                } else {
                        $iv = $calc_data;
                        $source_data = $old_calc_data;
                }
+
         }
+        close $CO;
+        close $CI;
+        waitpid $pid, 0;
 
        return $out;
 }
@@ -1133,13 +1163,14 @@ sub hash_mct($$) {
        my $out = "";
 
        $out .= "Seed = $pt\n\n";
-       
+
         for (my $j=0; $j<100; ++$j) {
                $out .= "COUNT = $j\n";
                my $md0=$pt;
                my $md1=$pt;
                my $md2=$pt;
                for (my $i=0; $i<1000; ++$i) {
+                       #print STDERR "outer loop $j; inner loop $i\n";
                        my $mi= $md0 . $md1 . $md2;
                        $md0=$md1;
                        $md1=$md2;
@@ -1164,10 +1195,10 @@ sub rsa_siggen($$$) {
        my $keyfile = shift;
 
        my $out = "";
-       
+
        $out .= "SHAAlg = $cipher\n";
        $out .= "Msg = $data\n";
-       $out .= "S = " . &$rsa_sign($data, $cipher, $keyfile) . "\n";
+       $out .= "S = " . &$rsa_sign($data, lc($cipher), $keyfile) . "\n";
 
        return $out;
 }
@@ -1204,7 +1235,7 @@ sub rsa_sigver($$$$$) {
        print FH hex2bin($signature);
        close FH;
 
-       $out .= "Result = " . (&$rsa_verify($data, $cipher, $keyfile, $sigfile) ? "P\n" : "F\n");
+       $out .= "Result = " . (&$rsa_verify($data, lc($cipher), $keyfile, $sigfile) ? "P\n" : "F\n");
 
        unlink($keyfile);
        unlink($sigfile);
@@ -1392,44 +1423,50 @@ sub parse($$) {
                                }
                        }
 
-
+                       if ($tt == 0) {
                        ##### Identify the test type
-                       if ($tmpline =~ /Hash sizes tested/) {
-                               $tt = 9;
-                               die "Interface function hmac for HMAC testing not defined for tested library"
-                                       if (!defined($hmac));
-                       } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /MCT/) {
-                               $tt = 8;
-                               die "Interface function state_rng for RNG MCT not defined for tested library"
-                                       if (!defined($state_rng));
-                       } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /VST/) {
-                               $tt = 7;
-                               die "Interface function state_rng for RNG KAT not defined for tested library"
-                                       if (!defined($state_rng));
-                       } elsif ($tmpline =~ /SigVer/ ) {
-                               $tt = 6;
-                               die "Interface function rsa_verify or gen_rsakey for RSA verification not defined for tested library"
-                                       if (!defined($rsa_verify) || !defined($gen_rsakey));
-                       } elsif ($tmpline =~ /SigGen/ ) {
-                               $tt = 5;
-                               die "Interface function rsa_sign or gen_rsakey for RSA sign not defined for tested library"
-                                       if (!defined($rsa_sign) || !defined($gen_rsakey));
-                       } elsif ($tmpline =~ /Monte|MCT|Carlo/ && $cipher eq "sha") {
-                               $tt = 4;
-                               die "Interface function hash for Hashing not defined for tested library"
-                                       if (!defined($hash));
-                       } elsif ($tmpline =~ /Monte|MCT|Carlo/) {
-                               $tt = 2;
-                               die "Interface function state_cipher for Stateful Cipher operation defined for tested library"
-                                       if (!defined($state_cipher));
-                       } elsif ($cipher =~ /^sha\d+/ && $tt!=5 && $tt!=6) {
-                               $tt = 3;
-                               die "Interface function hash for Hashing not defined for tested library"
-                                       if (!defined($hash));
-                       } else {
-                               $tt = 1;
-                               die "Interface function encdec for Encryption/Decryption not defined for tested library"
-                                       if (!defined($encdec));
+                               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/) {
+                                       $tt = 9;
+                                       die "Interface function hmac for HMAC testing not defined for tested library"
+                                               if (!defined($hmac));
+                               } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /MCT/) {
+                                       $tt = 8;
+                                       die "Interface function state_rng for RNG MCT not defined for tested library"
+                                               if (!defined($state_rng));
+                               } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /VST/) {
+                                       $tt = 7;
+                                       die "Interface function state_rng for RNG KAT not defined for tested library"
+                                               if (!defined($state_rng));
+                               } elsif ($tmpline =~ /SigVer/ ) {
+                                       $tt = 6;
+                                       die "Interface function rsa_verify or gen_rsakey for RSA verification not defined for tested library"
+                                               if (!defined($rsa_verify) || !defined($gen_rsakey));
+                               } elsif ($tmpline =~ /SigGen/ ) {
+                                       $tt = 5;
+                                       die "Interface function rsa_sign or gen_rsakey for RSA sign not defined for tested library"
+                                               if (!defined($rsa_sign) || !defined($gen_rsakey));
+                               } elsif ($tmpline =~ /Monte|MCT|Carlo/ && $cipher =~ /^sha/) {
+                                       $tt = 4;
+                                       die "Interface function hash for Hashing not defined for tested library"
+                                               if (!defined($hash));
+                               } elsif ($tmpline =~ /Monte|MCT|Carlo/) {
+                                       $tt = 2;
+                                       die "Interface function state_cipher for Stateful Cipher operation defined for tested library"
+                                               if (!defined($state_cipher));
+                               } elsif ($cipher =~ /^sha/) {
+                                       $tt = 3;
+                                       die "Interface function hash for Hashing not defined for tested library"
+                                               if (!defined($hash));
+                               } else {
+                                       $tt = 1;
+                                       die "Interface function encdec for Encryption/Decryption not defined for tested library"
+                                               if (!defined($encdec));
+                               }
                        }
                }
 
@@ -1449,11 +1486,19 @@ sub parse($$) {
                }
 
                # Get the test data
-               if ($line =~ /^(KEY|KEYs|KEY1|Key)\s*=\s*(.*)/) { # found in ciphers and RNG
+               if ($line =~ /^(KEY|KEY1|Key)\s*=\s*(.*)/) { # found in ciphers and RNG
+                       die "KEY seen twice - input file crap" if ($key1 ne "");
+                       $keytype=$1;
+                       $key1=$2;
+                       $key1 =~ s/\s//g; #replace potential white spaces
+               }
+               elsif ($line =~ /^(KEYs)\s*=\s*(.*)/) { # found in ciphers and RNG
                        die "KEY seen twice - input file crap" if ($key1 ne "");
                        $keytype=$1;
                        $key1=$2;
                        $key1 =~ s/\s//g; #replace potential white spaces
+                       $key2 = $key1;
+                       $key3 = $key1;
                }
                elsif ($line =~ /^KEY2\s*=\s*(.*)/) { # found in TDES
                        die "First key not set, but got already second key - input file crap" if ($key1 eq "");
index fdffdbb..9d3d5ba 100644 (file)
@@ -70,9 +70,6 @@ static int base64_output;
 /* We need to know whetehr we are in loop_mode.  */
 static int loop_mode;
 
-/* If true the input vectors are printed before and after encryption
-   and decryption.  */
-static int print_ivs;
 
 /* ASN.1 classes.  */
 enum
@@ -212,6 +209,78 @@ hex2buffer (const char *string, size_t *r_length)
   return buffer;
 }
 
+
+static char *
+read_textline (FILE *fp)
+{
+  char line[256];
+  char *p;
+  int any = 0;
+
+  /* Read line but skip over initial empty lines.  */
+  do
+    {
+      do 
+        {
+          if (!fgets (line, sizeof line, fp))
+            {
+              if (feof (fp))
+                return NULL;
+              die ("error reading input line: %s\n", strerror (errno));
+            }
+          p = strchr (line, '\n');
+          if (p)
+            *p = 0;
+          p = line + (*line? (strlen (line)-1):0);
+          for ( ;p > line; p--)
+            if (my_isascii (*p) && isspace (*p))
+              *p = 0;
+        }
+      while (!any && !*line);
+      any = 1;
+    }
+  while (*line == '#');  /* Always skip comment lines.  */
+  return gcry_xstrdup (line);
+}
+
+static char *
+read_hexline (FILE *fp, size_t *retlen)
+{
+  char *line, *p;
+
+  line = read_textline (fp);
+  if (!line)
+    return NULL;
+  p = hex2buffer (line, retlen);
+  if (!p)
+    die ("error decoding hex string on input\n");
+  gcry_free (line);
+  return p;
+}
+
+static void
+skip_to_empty_line (FILE *fp)
+{
+  char line[256];
+  char *p;
+
+  do
+    {
+      if (!fgets (line, sizeof line, fp))
+        {
+          if (feof (fp))
+            return;
+          die ("error reading input line: %s\n", strerror (errno));
+        }
+      p = strchr (line, '\n');
+      if (p)
+        *p =0;
+    }
+  while (*line);
+}
+
+
+
 /* Read a file from stream FP into a newly allocated buffer and return
    that buffer.  The valid length of the buffer is stored at R_LENGTH.
    Returns NULL on failure.  If decode is set, the file is assumed to
@@ -909,52 +978,15 @@ run_encrypt_decrypt (int encrypt_mode,
       else
         inbuflen = datalen;
 
-      if (print_ivs)
-        {
-          /* If we want to print the input vectors we need to pass the
-             data block by block to the encryption function.  */
-          unsigned char tmp[17];
-          const unsigned char *iptr = data;
-          size_t ilen;
-          
-          do
-            {
-              ilen = inbuflen > blocklen? blocklen : inbuflen;
-              
-              if (gcry_cipher_ctl (hd, PRIV_CTL_GET_INPUT_VECTOR,
-                                   tmp, sizeof tmp))
-                die ("error getting input block\n");
-              print_buffer (tmp+1, *tmp);
-              putchar ('\n');
-
-              if (encrypt_mode)
-                err = gcry_cipher_encrypt (hd, outbuf, blocklen, iptr, ilen);
-              else
-                err = gcry_cipher_decrypt (hd, outbuf, blocklen, iptr, ilen);
-              if (err)
-                die ("gcry_cipher_%scrypt failed: %s\n",
-                     encrypt_mode? "en":"de", gpg_strerror (err));
-              
-              print_buffer (outbuf, blocklen);
-              putchar ('\n');
-
-              iptr += ilen;
-              inbuflen -= ilen;
-             }
-          while (inbuflen);
-        }
+      if (encrypt_mode)
+        err = gcry_cipher_encrypt (hd, outbuf, outbuflen, data, inbuflen);
       else
-        {
-          if (encrypt_mode)
-            err = gcry_cipher_encrypt (hd, outbuf, outbuflen, data, inbuflen);
-          else
-            err = gcry_cipher_decrypt (hd, outbuf, outbuflen, data, inbuflen);
-          if (err)
-            die ("gcry_cipher_%scrypt failed: %s\n",
-                 encrypt_mode? "en":"de", gpg_strerror (err));
-          
-          print_buffer (outbuf, outbuflen);
-        }
+        err = gcry_cipher_decrypt (hd, outbuf, outbuflen, data, inbuflen);
+      if (err)
+        die ("gcry_cipher_%scrypt failed: %s\n",
+             encrypt_mode? "en":"de", gpg_strerror (err));
+      
+      print_buffer (outbuf, outbuflen);
     }
   while (inbuf);
 
@@ -964,6 +996,116 @@ run_encrypt_decrypt (int encrypt_mode,
 }
 
 
+static void
+get_current_iv (gcry_cipher_hd_t hd, void *buffer, size_t buflen)
+{
+  unsigned char tmp[17];
+
+  if (gcry_cipher_ctl (hd, PRIV_CTL_GET_INPUT_VECTOR, tmp, sizeof tmp))
+    die ("error getting current input vector\n");
+  if (buflen > *tmp)
+    die ("buffer too short to store the current input vector\n");
+  memcpy (buffer, tmp+1, *tmp);
+}
+
+/* Run the inner loop of the CAVS monte carlo test.  */
+static void
+run_cipher_mct_loop (int encrypt_mode, int cipher_algo, int cipher_mode, 
+                     const void *iv_buffer, size_t iv_buflen,
+                     const void *key_buffer, size_t key_buflen,
+                     const void *data, size_t datalen, int iterations)
+{
+  gpg_error_t err;
+  gcry_cipher_hd_t hd;
+  size_t blocklen;
+  int count;
+  char input[16];
+  char output[16];
+  char last_output[16];
+  char last_last_output[16];
+  char last_iv[16]; 
+
+
+  err = gcry_cipher_open (&hd, cipher_algo, cipher_mode, 0);
+  if (err)
+    die ("gcry_cipher_open failed for algo %d, mode %d: %s\n", 
+         cipher_algo, cipher_mode, gpg_strerror (err));
+
+  blocklen = gcry_cipher_get_algo_blklen (cipher_algo);
+  if (!blocklen || blocklen > sizeof output)
+    die ("invalid block length %d\n", blocklen);
+
+
+  gcry_cipher_ctl (hd, PRIV_CTL_DISABLE_WEAK_KEY, NULL, 0);
+
+  err = gcry_cipher_setkey (hd, key_buffer, key_buflen);
+  if (err)
+    die ("gcry_cipher_setkey failed with keylen %u: %s\n",
+         (unsigned int)key_buflen, gpg_strerror (err));
+
+  if (iv_buffer)
+    {
+      err = gcry_cipher_setiv (hd, iv_buffer, iv_buflen);
+      if (err)
+        die ("gcry_cipher_setiv failed with ivlen %u: %s\n",
+             (unsigned int)iv_buflen, gpg_strerror (err));
+    }
+
+  if (datalen != blocklen)
+    die ("length of input (%u) does not match block length (%u)\n", 
+         (unsigned int)datalen, (unsigned int)blocklen);
+  memcpy (input, data, datalen);
+  memset (output, 0, sizeof output);
+  for (count=0; count < iterations; count++)
+    {
+      memcpy (last_last_output, last_output, sizeof last_output);
+      memcpy (last_output, output, sizeof output);
+
+      get_current_iv (hd, last_iv, blocklen);
+
+      if (encrypt_mode)
+        err = gcry_cipher_encrypt (hd, output, blocklen, input, blocklen);
+      else
+        err = gcry_cipher_decrypt (hd, output, blocklen, input, blocklen);
+      if (err)
+        die ("gcry_cipher_%scrypt failed: %s\n",
+             encrypt_mode? "en":"de", gpg_strerror (err));
+
+  
+      if (encrypt_mode && (cipher_mode == GCRY_CIPHER_MODE_CFB
+                           || cipher_mode == GCRY_CIPHER_MODE_CBC))
+        memcpy (input, last_iv, blocklen);
+      else if (cipher_mode == GCRY_CIPHER_MODE_OFB)
+        memcpy (input, last_iv, blocklen);
+      else if (!encrypt_mode && cipher_mode == GCRY_CIPHER_MODE_CFB)
+        {
+          /* Reconstruct the output vector.  */
+          int i;
+          for (i=0; i < blocklen; i++)
+            input[i] ^= output[i];
+        }
+      else
+        memcpy (input, output, blocklen);
+    }
+
+  print_buffer (output, blocklen);
+  putchar ('\n');
+  print_buffer (last_output, blocklen);
+  putchar ('\n');
+  print_buffer (last_last_output, blocklen);
+  putchar ('\n');
+  get_current_iv (hd, last_iv, blocklen);
+  print_buffer (last_iv, blocklen); /* Last output vector.  */
+  putchar ('\n');
+  print_buffer (input, blocklen);   /* Next input text. */
+  putchar ('\n');
+  putchar ('\n');
+  fflush (stdout);
+
+  gcry_cipher_close (hd);
+}
+
+
 \f
 /* Run a digest operation.  */
 static void
@@ -1367,7 +1509,7 @@ usage (int show_help)
      "  --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"
-     "  --print-ivs      Print input vectors\n"
+     "  --mct-server     Run a monte carlo test server\n"
      "  --loop           Enable random loop mode\n"
      "  --progress       Print pogress indicators\n"
      "  --help           Print this text\n"
@@ -1395,6 +1537,7 @@ main (int argc, char **argv)
   void *data;
   size_t datalen;
   size_t chunksize = 0;
+  int mct_server = 0;
 
 
   if (argc)
@@ -1504,9 +1647,9 @@ main (int argc, char **argv)
           use_pkcs1 = 1;
           argc--; argv++;
         }
-      else if (!strcmp (*argv, "--print-ivs"))
+      else if (!strcmp (*argv, "--mct-server"))
         {
-          print_ivs = 1;
+          mct_server = 1;
           argc--; argv++;
         }
     }          
@@ -1544,6 +1687,7 @@ main (int argc, char **argv)
 
   /* Most operations need some input data.  */
   if (!chunksize
+      && !mct_server
       && strcmp (mode_string, "random")
       && strcmp (mode_string, "rsa-gen") )
     {
@@ -1564,7 +1708,8 @@ main (int argc, char **argv)
   if (!strcmp (mode_string, "encrypt") || !strcmp (mode_string, "decrypt"))
     {
       int cipher_algo, cipher_mode;
-      void  *iv_buffer, *key_buffer;
+      void  *iv_buffer = NULL;
+      void *key_buffer = NULL;
       size_t iv_buflen,  key_buflen;
 
       if (!algo_string)
@@ -1572,30 +1717,70 @@ main (int argc, char **argv)
       cipher_algo = map_openssl_cipher_name (algo_string, &cipher_mode);
       if (!cipher_algo)
         die ("cipher algorithm `%s' is not supported\n", algo_string);
-      if (cipher_mode != GCRY_CIPHER_MODE_ECB)
+      if (mct_server)
         {
-          if (!iv_string)
-            die ("option --iv is required in this mode\n");
-          iv_buffer = hex2buffer (iv_string, &iv_buflen);
-          if (!iv_buffer)
-            die ("invalid value for IV\n");
+          int iterations;
+
+          for (;;)
+            {
+              gcry_free (key_buffer); key_buffer = NULL;
+              gcry_free (iv_buffer); iv_buffer = NULL;
+              gcry_free (data); data = NULL;
+              if (!(key_buffer = read_textline (input)))
+                {
+                  if (feof (input))
+                    break;
+                  die ("no version info in input\n");
+                }
+              if (atoi (key_buffer) != 1)
+                die ("unsupported input version %s\n", key_buffer);
+              gcry_free (key_buffer);
+              if (!(key_buffer = read_textline (input)))
+                die ("no iteration count in input\n");
+              iterations = atoi (key_buffer);
+              gcry_free (key_buffer);
+              if (!(key_buffer = read_hexline (input, &key_buflen)))
+                die ("no key in input\n");
+              if (!(iv_buffer = read_hexline (input, &iv_buflen)))
+                die ("no IV in input\n");
+              if (!(data = read_hexline (input, &datalen)))
+                die ("no data in input\n");
+              skip_to_empty_line (input);
+              
+              run_cipher_mct_loop ((*mode_string == 'e'),
+                                   cipher_algo, cipher_mode,
+                                   iv_buffer, iv_buflen,
+                                   key_buffer, key_buflen,
+                                   data, datalen, iterations);
+            }
         }
       else
         {
-          iv_buffer = NULL;
-          iv_buflen = 0;
+          if (cipher_mode != GCRY_CIPHER_MODE_ECB)
+            {
+              if (!iv_string)
+                die ("option --iv is required in this mode\n");
+              iv_buffer = hex2buffer (iv_string, &iv_buflen);
+              if (!iv_buffer)
+                die ("invalid value for IV\n");
+            }
+          else
+            {
+              iv_buffer = NULL;
+              iv_buflen = 0;
+            }
+          if (!key_string)
+            die ("option --key is required in this mode\n");
+          key_buffer = hex2buffer (key_string, &key_buflen);
+          if (!key_buffer)
+            die ("invalid value for KEY\n");
+
+          run_encrypt_decrypt ((*mode_string == 'e'),
+                               cipher_algo, cipher_mode,
+                               iv_buffer, iv_buflen,
+                               key_buffer, key_buflen,
+                               data, data? datalen:chunksize, input);
         }
-      if (!key_string)
-        die ("option --key is required in this mode\n");
-      key_buffer = hex2buffer (key_string, &key_buflen);
-      if (!key_buffer)
-        die ("invalid value for KEY\n");
-
-      run_encrypt_decrypt ((*mode_string == 'e'),
-                           cipher_algo, cipher_mode,
-                           iv_buffer, iv_buflen,
-                           key_buffer, key_buflen,
-                           data, data? datalen:chunksize, input);
       gcry_free (key_buffer);
       gcry_free (iv_buffer);
     }
@@ -1608,6 +1793,8 @@ main (int argc, char **argv)
       algo = gcry_md_map_name (algo_string);
       if (!algo)
         die ("digest algorithm `%s' is not supported\n", algo_string);
+      if (!data)
+        die ("no data available (do not use --chunk)\n");
 
       run_digest (algo, data, datalen);
     }