Another tweak for the RNG test code.
authorWerner Koch <wk@gnupg.org>
Tue, 16 Sep 2008 19:22:10 +0000 (19:22 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 16 Sep 2008 19:22:10 +0000 (19:22 +0000)
13 files changed:
cipher/ChangeLog
cipher/ecc.c
doc/gcrypt.texi
random/ChangeLog
random/rand-internal.h
random/random-fips.c
random/random.c
src/ChangeLog
src/global.c
tests/ChangeLog
tests/Makefile.am
tests/cavs_driver.pl [new file with mode: 0644]
tests/fipsrngdrv.c

index 25a9cd3..502fb4c 100644 (file)
@@ -1,3 +1,7 @@
+2008-09-16  Werner Koch  <wk@g10code.com>
+
+       * ecc.c (run_selftests): Add arg EXTENDED.
+
 2008-09-12  Werner Koch  <wk@g10code.com>
 
        * rsa.c (test_keys): Do a bad case signature check.
index 5d5c322..c90391f 100644 (file)
@@ -1316,10 +1316,12 @@ selftests_ecdsa (selftest_report_func_t report)
 
 /* Run a full self-test for ALGO and return 0 on success.  */
 static gpg_err_code_t
-run_selftests (int algo, selftest_report_func_t report)
+run_selftests (int algo, int extended, selftest_report_func_t report)
 {
   gpg_err_code_t ec;
 
+  (void)extended;
+
   switch (algo)
     {
     case GCRY_PK_ECDSA:
index 1608d6c..e5f3de0 100644 (file)
@@ -5249,9 +5249,9 @@ The application may requests tests at any time by means of the
 @code{GCRYCTL_SELFTEST} control command.  Note that using these tests
 is not FIPS conform: Although Libgcrypt rejects all application
 requests for services while running self-tests, it does not ensure
-that no other operations of Libgcrypt are still being executed.  Thus
-in FIPS mode an application requesting self-tests needs to be
-power-cycle Libgcrypt instead.
+that no other operations of Libgcrypt are still being executed.  Thus,
+in FIPS mode an application requesting self-tests needs to power-cycle
+Libgcrypt instead.
 
 When self-tests are requested, Libgcrypt runs all the tests it does
 during power-up as well as a few extra checks as described below.
index c22ea2e..1b6c468 100644 (file)
@@ -2,6 +2,11 @@
 
        * random-fips.c (x931_aes_driver): No re-seeding with test contexts.
        (_gcry_rngfips_init_external_test): Fix setting of test_dt_ptr.
+       (struct rng_context): Add flag TEST_NO_DUP_CHECK.
+       (x931_aes_driver): Use that flag.
+       (_gcry_rngfips_init_external_test): Add arg FLAGS and use it to
+       modify the test.
+       * random.c (_gcry_random_init_external_test): Pass FLAGS.
 
 2008-09-15  Werner Koch  <wk@g10code.com>
 
index 269fad9..534d828 100644 (file)
@@ -79,7 +79,8 @@ void _gcry_rngfips_create_nonce (void *buffer, size_t length);
 
 gcry_error_t _gcry_rngfips_selftest (selftest_report_func_t report);
 
-gcry_err_code_t _gcry_rngfips_init_external_test (void **r_context, 
+gcry_err_code_t _gcry_rngfips_init_external_test (void **r_context,
+                                                  unsigned int flags,
                                                   const void *key,
                                                   size_t keylen,
                                                   const void *seed,
index 2fc9596..2667e71 100644 (file)
@@ -141,13 +141,16 @@ struct rng_context
 
   unsigned char guard_3[1];
 
+  /* The external test may want to suppress the duplicate bock check.
+     This is done if the this flag is set.  */
+  unsigned char test_no_dup_check;
   /* To implement a KAT we need to provide a know DT value.  To
      accomplish this the x931_get_dt function checks whether this
      field is not NULL and then uses the 16 bytes at this address for
      the DT value.  However the last 4 bytes are replaced by the
      value of field TEST_DT_COUNTER which will be incremented after
      each invocation of x931_get_dt. We use a pointer and not a buffer
-     because there is no need to put this value into secure  memory.  */
+     because there is no need to put this value into secure memory.  */
   const unsigned char *test_dt_ptr;
   u32 test_dt_counter;
 
@@ -476,24 +479,36 @@ x931_aes_driver (unsigned char *output, size_t length, rng_context_t rng_ctx)
                 intermediate_I, temp_buffer);
       rng_ctx->use_counter++;
 
-      /* Do a basic check on the output to avoid a stuck generator.  */
-      if (!rng_ctx->compare_value_valid)
+      if (rng_ctx->test_no_dup_check
+          && rng_ctx->test_dt_ptr
+          && rng_ctx != nonce_context
+          && rng_ctx != std_rng_context
+          && rng_ctx != strong_rng_context)
         {
-          /* First time used, only save the result.  */
-          memcpy (rng_ctx->compare_value, result_buffer, 16);
-          rng_ctx->compare_value_valid = 1;
-          continue;
+          /* This is a test context which does not want the duplicate
+             block check. */
         }
-      if (!memcmp (rng_ctx->compare_value, result_buffer, 16))
+      else
         {
-          /* Ooops, we received the same 128 bit block - that should
-             in theory never happen.  The FIPS requirement says that
-             we need to put ourself into the error state in such
-             case.  */
-          fips_signal_error ("duplicate 128 bit block returned by RNG");
-          return -1;
+          /* Do a basic check on the output to avoid a stuck generator.  */
+          if (!rng_ctx->compare_value_valid)
+            {
+              /* First time used, only save the result.  */
+              memcpy (rng_ctx->compare_value, result_buffer, 16);
+              rng_ctx->compare_value_valid = 1;
+              continue;
+            }
+          if (!memcmp (rng_ctx->compare_value, result_buffer, 16))
+            {
+              /* Ooops, we received the same 128 bit block - that should
+                 in theory never happen.  The FIPS requirement says that
+                 we need to put ourself into the error state in such
+                 case.  */
+              fips_signal_error ("duplicate 128 bit block returned by RNG");
+              return -1;
+            }
+          memcpy (rng_ctx->compare_value, result_buffer, 16);
         }
-      memcpy (rng_ctx->compare_value, result_buffer, 16);
       
       /* Append to outbut.  */
       memcpy (output, result_buffer, nbytes);
@@ -1002,7 +1017,7 @@ _gcry_rngfips_selftest (selftest_report_func_t report)
    success the test context is stored at R_CONTEXT; on failure NULL is
    stored at R_CONTEXT and an error code is returned.  */
 gcry_err_code_t
-_gcry_rngfips_init_external_test (void **r_context,
+_gcry_rngfips_init_external_test (void **r_context, unsigned int flags,
                                   const void *key, size_t keylen,
                                   const void *seed, size_t seedlen,
                                   const void *dt, size_t dtlen)
@@ -1051,6 +1066,9 @@ _gcry_rngfips_init_external_test (void **r_context,
                                |(test_ctx->test_dt_ptr[14] << 8)
                                |(test_ctx->test_dt_ptr[15]) );
 
+  if ( (flags & 1) )
+    test_ctx->test_no_dup_check = 1;
+
   check_guards (test_ctx);
   /* All fine.  */
   err = 0;
index 9da83cf..85d5fa9 100644 (file)
@@ -296,7 +296,7 @@ _gcry_random_init_external_test (void **r_context,
 {
   (void)flags;
   if (fips_mode ())
-    return _gcry_rngfips_init_external_test (r_context, key, keylen,
+    return _gcry_rngfips_init_external_test (r_context, flags, key, keylen,
                                              seed, seedlen,
                                              dt, dtlen);
   else
index 04327cb..b96f8bb 100644 (file)
@@ -1,5 +1,7 @@
 2008-09-16  Werner Koch  <wk@g10code.com>
 
+       * global.c (print_config): Use y/n for fips mode.
+
        * fips.c (fips_new_state): Allow transition to Error and
        Fatal-error from Init.
 
index ce54f0d..4af7412 100644 (file)
@@ -294,7 +294,10 @@ print_config ( int (*fnc)(FILE *fp, const char *format, ...), FILE *fp)
   if ( (hwf & hwflist[i].flag) )
     fnc (fp, "%s:", hwflist[i].desc);
   fnc (fp, "\n");
-  fnc (fp, "fips-mode:%d:\n", fips_mode () );
+  /* We use y/n instead of 1/0 for the simple reason that Emacsen's
+     compile error parser would accidently flag that line when printed
+     during "make check" as an error.  */
+  fnc (fp, "fips-mode:%c:\n", fips_mode ()? 'y':'n' );
 }
 
 
index c24fb5b..de5381d 100644 (file)
@@ -2,6 +2,7 @@
 
        * fipsrngdrv.c (main): Bail out on write error.  Implement verbose
        option.
+       (main): Use flag to disable dup block checks.
 
 2008-09-15  Werner Koch  <wk@g10code.com>
 
index 509647a..21d5bd8 100644 (file)
@@ -43,3 +43,6 @@ noinst_PROGRAMS = $(TESTS) fipsrngdrv
 
 EXTRA_DIST = README rsa-16k.key
 
+# Note: There is a file cavs-driver.pl in the SVN but we do not
+# distribute it because we have no configure tests for Perl and thus
+# we can expect that people get it from the SVN instead.
diff --git a/tests/cavs_driver.pl b/tests/cavs_driver.pl
new file mode 100644 (file)
index 0000000..3ebd2e3
--- /dev/null
@@ -0,0 +1,1498 @@
+#!/usr/bin/env perl
+#
+# CAVS test driver (based on the OpenSSL driver)
+# Written by: Stephan Müller <sm@atsec.com>
+# Copyright (c) atsec information security corporation
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions: 
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+#                            NO WARRANTY
+#
+#    BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+#    FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+#    OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+#    PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+#    OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+#    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+#    TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+#    PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+#    REPAIR OR CORRECTION.
+#
+#    IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+#    WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+#    REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+#    INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+#    OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+#    TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+#    YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+#    PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+#    POSSIBILITY OF SUCH DAMAGES.
+#
+#
+# test execution instruction:
+# 1. get the request files from the lab
+# 2. call each request file from 1. with this program:
+#      $0 <FILE>.rep
+# 3. send the resulting file <FILE>.rsp to the lab
+#
+#
+# Test should be easily adoptable to other implementations
+# See the first functions for this task
+#
+# Following tests are covered (others may also be covered
+# but have not been tested)
+#
+# AES
+#      CBCGFSbox[128|192|256]
+#      CBCMCT[128|192|256]
+#      CBCVarKey[128|192|256]
+#      CBCKeySbox[128|192|256]
+#      CBCMMT[128|192|256]
+#      CBCVarTxt[128|192|256]
+#
+# RSA
+#      SigGen[15|RSA]
+#      SigVer15
+#      (SigVerRSA is not applicable for OpenSSL as X9.31 padding
+#              is not done through openssl dgst)
+#
+# SHA
+#      SHA1ShortMsg
+#      SHA1LongMsg
+#      SHA1Monte
+#
+# TDES
+#      TCBCMonte[1|2|3]
+#      TCBCpermop
+#      TCBCMMT[1|2|3]
+#      TCBCsubtab
+#      TCBCvarkey
+#      TCBCinvperm
+#      TCBCvartext
+#
+# ANSI X9.31 RNG
+#      ANSI931_AES128MCT
+#      ANSI931_AES128VST
+#
+# RC4 (atsec developed tests)
+#      RC4KeyBD
+#      RC4MCT
+#      RC4PltBD
+#      RC4REGT
+#
+
+use strict;
+use warnings;
+use IPC::Open2;
+use Getopt::Std;
+use MIME::Base64;
+
+# Contains the command line options
+my %opt;
+
+
+#################################################################
+##### Central interface functions to the external ciphers #######
+#################################################################
+# Only these interface routines should be changed in case of
+# porting to a new cipher library
+#
+# For porting to a new library, create implementation of these functions
+# and then add pointers to the respective implementation of each
+# function to the given variables.
+
+# common encryption/decryption routine
+# $1 key in hex form
+# $2 iv in hex form
+# $3 cipher
+# $4 encrypt=1/decrypt=0
+# $5 de/encrypted data in hex form
+# return en/decrypted data in hex form
+my $encdec;
+
+# Sign a message with RSA
+# $1: data to be signed in hex form
+# $2: Hash algo
+# $3: Key file in PEM format with the private key
+# return: digest in hex format
+my $rsa_sign;
+
+# Verify a message with RSA
+# $1: data to be verified in hex form
+# $2: hash algo
+# $3: file holding the public RSA key in PEM format
+# $4: file holding the signature in binary form
+# return: 1 == verfied / 0 == not verified
+my $rsa_verify;
+
+# generate a new private RSA key in PEM format
+# $1 key size in bit
+# $2 keyfile name
+# return: nothing, but file created
+my $gen_rsakey;
+
+# Creating a hash
+# $1: Plaintext
+# $2: hash type
+# return: hash in hex form
+my $hash;
+
+# supplying the call to the external cipher implementation
+# that is being used to keep STDIN and STDOUT open
+# to maintain the state of the block chaining
+# $1: cipher
+# $2: 1=encryption, 0=decryption
+# $3: buffersize needed for openssl
+# $4: encryption key in binary form
+# $5: IV in binary form
+# return: command line to execute the application
+my $state_cipher;
+
+# supplying the call to the external cipher implementation
+# that is being used to keep STDIN and STDOUT open
+# to maintain the state of the RNG with its seed
+#
+# input holds seed values
+# $1: cipher key in hex format
+# $2: DT value in hex format
+# $3: V value in hex format
+#
+# return: command line to execute the application
+#
+# the application is expected to deliver random values on STDOUT - the script
+# reads 128 bits repeatedly where the state of the RNG must be retained
+# between the reads. The output of the RNG on STDOUT is assumed to be binary.
+my $state_rng;
+
+################################################################
+##### OpenSSL interface functions
+################################################################
+sub openssl_encdec($$$$$) {
+       my $key=shift;
+       my $iv=shift;
+       my $cipher=shift;
+       my $enc = (shift) ? "-e" : "-d";
+       my $data=shift;
+
+       $data=hex2bin($data);
+       my $program="openssl enc -$cipher -nopad -nosalt -K $key $enc -iv $iv";
+       $program = "rc4 -k $key" if $opt{'R'}; #for ARCFOUR, no IV must be given
+       $data=pipe_through_program($data,$program);
+       return bin2hex($data);
+}
+
+sub openssl_rsa_sign($$$) {
+       my $data = shift;
+       my $cipher = shift;
+       my $keyfile = shift;
+
+       $data=hex2bin($data);
+       die "ARCFOUR not available for RSA" if $opt{'R'};
+       $data=pipe_through_program($data,
+               "openssl dgst -$cipher -binary -sign $keyfile");
+       return bin2hex($data);
+}
+
+sub openssl_rsa_verify($$$$) {
+       my $data = shift;
+       my $cipher = shift;
+       my $keyfile = shift;
+       my $sigfile = shift;
+
+       $data = hex2bin($data);
+       die "ARCFOUR not available for RSA" if $opt{'R'};
+       $data = pipe_through_program($data,
+               "openssl dgst -$cipher -binary -verify $keyfile -signature $sigfile");
+
+       # Parse through the OpenSSL output information
+       return ($data =~ /OK/);
+}
+
+sub openssl_gen_rsakey($$) {
+       my $keylen = shift;
+       my $file = shift;
+
+       die "ARCFOUR not available for RSA" if $opt{'R'};
+       # generating of a key with exponent 0x10001
+       my @args = ("openssl", "genrsa", "-F4", "-out", "$file", "$keylen");
+        system(@args) == 0
+               or die "system @args failed: $?";
+       die "system @args failed: file $file not created" if (! -f $file);
+}
+
+sub openssl_hash($$) {
+       my $pt = shift;
+       my $cipher = shift;
+
+       die "ARCFOUR not available for hashes" if $opt{'R'};
+       my $hash = hex2bin($pt);
+       #bin2hex not needed as the '-hex' already converts it
+       return pipe_through_program($hash, "openssl dgst -$cipher -hex");
+}
+
+sub openssl_state_cipher($$$$$) {
+       my $cipher = shift;
+       my $encdec = shift;
+       my $bufsize = shift;
+       my $key = shift;
+       my $iv = shift;
+
+       my $enc = $encdec ? "-e": "-d";
+
+       my $out = "openssl enc -'$cipher' $enc -nopad -nosalt -bufsize $bufsize -K ".bin2hex($key)." -iv ".bin2hex($iv);
+       #for ARCFOUR, no IV must be given
+       $out = "rc4 -k " . bin2hex($key) if $opt{'R'};
+       return $out;
+}
+
+###### End of OpenSSL interface implementation ############
+
+###########################################################
+###### libgcrypt implementation
+###########################################################
+sub libgcrypt_state_rng($$$) {
+       my $key = shift;
+       my $dt = shift;
+       my $v = shift;
+
+       return "fipsrngdrv --binary --loop $key $v $dt";
+}
+
+######### End of libgcrypt implementation ################
+
+##### No other interface functions below this point ######
+##########################################################
+
+##########################################################
+# General helper routines
+
+# Executing a program by feeding STDIN and retrieving
+# STDOUT
+# $1: data string to be piped to the app on STDIN
+# rest: program and args
+# returns: STDOUT of program as string
+sub pipe_through_program($@) {
+       my $in = shift;
+       my @args = @_;
+
+       my ($CO, $CI);
+       my $pid = open2($CO, $CI, @args);
+       
+       my $out = "";
+       my $len = length($in);
+       my $first = 1;
+       while (1) {
+               my $rin = "";
+               my $win = "";
+               # Output of prog is FD that we read
+               vec($rin,fileno($CO),1) = 1;
+               # Input of prog is FD that we write
+               # check for $first is needed because we can have NULL input
+               # that is to be written to the app
+               if ( $len > 0 || $first) {
+                       (vec($win,fileno($CI),1) = 1);
+                       $first=0;
+               }
+               # Let us wait for 100ms
+               my $nfound = select(my $rout=$rin, my $wout=$win, undef, 0.1);
+               if ( $wout ) {
+                       my $written = syswrite($CI, $in, $len);
+                       die "broken pipe" if !defined $written;
+                       $len -= $written;
+                       substr($in, 0, $written) = "";
+                       if ($len <= 0) {
+                               close $CI or die "broken pipe: $!";
+                       }
+               }
+               if ( $rout ) {
+                       my $tmp_out = "";
+                       my $bytes_read = sysread($CO, $tmp_out, 4096);
+                       $out .= $tmp_out;
+                       last if ($bytes_read == 0);
+               }
+       }
+       close $CO or die "broken pipe: $!";
+       waitpid $pid, 0;
+       
+       return $out;
+}
+
+#
+# convert ASCII hex to binary input
+# $1 ASCII hex
+# return binary representation
+sub hex2bin($) {
+       my $in = shift;
+       my $len = length($in);
+       $len = 0 if ($in eq "00");
+       return pack("H$len", "$in");
+}
+
+#
+# convert binary input to ASCII hex
+# $1 binary value
+# return ASCII hex representation
+sub bin2hex($) {
+       my $in = shift;
+       my $len = length($in)*2;
+       return unpack("H$len", "$in");
+}
+
+# $1: binary byte (character)
+# returns: binary byte with odd parity using low bit as parity bit
+sub odd_par($) {
+       my $in = ord(shift);
+       my $odd_count=0;
+       for(my $i=1; $i<8; $i++) {
+               $odd_count++ if ($in & (1<<$i));
+       }
+
+       my $out = $in;
+       if ($odd_count & 1) { # check if parity is already odd
+               $out &= ~1; # clear the low bit
+       } else {
+               $out |= 1; # set the low bit
+       }
+
+       return chr($out);
+}
+
+# DES keys uses only the 7 high bits of a byte, the 8th low bit
+# is the parity bit
+# as the new key is calculated from oldkey XOR cipher in the MCT test,
+# the parity is not really checked and needs to be set to match
+# expectation (OpenSSL does not really care, but the FIPS
+# test result is expected that the key has the appropriate parity)
+# $1: arbitrary binary string
+# returns: string with odd parity set in low bit of each byte
+sub fix_key_parity($) {
+       my $in = shift;
+       my $out = "";
+       for (my $i = 0; $i < length($in); $i++) {
+               $out .= odd_par(substr($in, $i, 1));
+       }
+
+       return $out;
+}
+
+####################################################
+# 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
+
+# Convert unsigned integer to base256 bigint bytes
+# $1 integer
+# returns base256 octet string
+sub int_base256_unsigned($) {
+       my $n = shift;
+
+       my $out = chr($n & 255);
+       while ($n>>=8) {
+               $out = chr($n & 255) . $out;
+       }
+
+       return $out;
+}
+
+# Convert signed integer to base256 bigint bytes
+# $1 integer
+# returns base256 octet string
+sub int_base256_signed($) {
+       my $n = shift;
+       my $negative = ($n < 0);
+
+       if ($negative) {
+               $n = -$n-1;
+       }
+
+       my $out = int_base256_unsigned($n);
+
+       if (ord(substr($out, 0, 1)) & 128) {
+               # it's supposed to be positive but has sign bit set,
+               # add a leading zero
+               $out = chr(0) . $out;
+       }
+
+       if ($negative) {
+               my $neg = chr(255) x length($out);
+               $out ^= $neg;
+       }
+
+       return $out;
+}
+
+# Length header for specified DER object length
+# $1 length as integer
+# return octet encoding for length
+sub der_len($) {
+       my $len = shift;
+
+       if ($len <= 127) {
+               return chr($len);
+       } else {
+               my $blen = int_base256_unsigned($len);
+
+               return chr(128 | length($blen)) . $blen;
+       }
+}
+
+# Prepend length header to object
+# $1 object as octet sequence
+# return length header for object followed by object as octets
+sub der_len_obj($) {
+       my $x = shift;
+
+       return der_len(length($x)) . $x;
+}
+
+# DER sequence
+# $* objects
+# returns DER sequence consisting of the objects passed as arguments
+sub der_seq {
+       my $seq = join("", @_);
+       return chr(0x30) . der_len_obj($seq);
+}
+
+# DER bitstring
+# $1 input octets (must be full octets, fractional octets not supported)
+# returns input encapsulated as bitstring
+sub der_bitstring($) {
+       my $x = shift;
+
+       $x = chr(0) . $x;
+
+       return chr(0x03) . der_len_obj($x);
+}
+
+# base-128-encoded integer, used for object numbers.
+# $1 integer
+# returns octet sequence
+sub der_base128($) {
+       my $n = shift;
+
+       my $out = chr($n & 127);
+
+       while ($n>>=7) {
+               $out = chr(128 | ($n & 127)) . $out;
+       }
+
+       return $out;
+}
+
+# Generating the PEM certificate string
+# (base-64-encoded DER string)
+# $1 DER string
+# returns octet sequence
+sub pem_cert($) {
+       my $n = shift;
+
+       my $out = "-----BEGIN PUBLIC KEY-----\n";
+       $out .= encode_base64($n);
+       $out .= "-----END PUBLIC KEY-----\n";
+
+       return $out;
+}
+
+# DER object identifier
+# $* sequence of id numbers
+# returns octets
+sub der_objectid {
+       my $v1 = shift;
+       my $v2 = shift;
+
+       my $out = chr(40*$v1 + $v2) . join("", map { der_base128($_) } @_);
+
+       return chr(0x06) . der_len_obj($out);
+}
+
+# DER signed integer
+# $1 number as octet string (base 256 representation, high byte first)
+# returns number in DER integer encoding
+sub der_bigint($) {
+       my $x = shift;
+
+       return chr(0x02) . der_len_obj($x);
+}
+
+# DER positive integer with leading zeroes stripped
+# $1 number as octet string (base 256 representation, high byte first)
+# returns number in DER integer encoding
+sub der_pos_bigint($) {
+       my $x = shift;
+
+       # strip leading zero digits
+       $x =~ s/^[\0]+//;
+
+       # need to prepend a zero if high bit set, since it would otherwise be
+       # interpreted as a negative number. Also needed for number 0.
+       if (!length($x) || ord(substr($x, 0, 1)) >= 128) {
+               $x = chr(0) . $x;
+       }
+
+       return der_bigint($x);
+}
+
+# $1 number as signed integer
+# returns number as signed DER integer encoding
+sub der_int($) {
+       my $n = shift;
+       
+       return der_bigint(int_base256_signed($n));
+}
+
+# the NULL object constant
+sub der_null() {
+       return chr(0x05) . chr(0x00);
+}
+
+# Unit test helper
+# $1 calculated result
+# $2 expected result
+# no return value, dies if results differ, showing caller's line number
+sub der_test($$) {
+       my $actual = bin2hex(shift);
+       my $expected = shift;
+
+       my @caller = caller;
+       $actual eq $expected or die "Error:line $caller[2]:assertion failed: "
+               ."$actual != $expected\n";
+}
+
+# Unit testing for the DER encoding functions
+# Examples from http://www.columbia.edu/~ariel/ssleay/layman.html
+# No input, no output. Dies if unit tests fail.
+sub der_unit_test {
+       ## uncomment these if you want to test the test framework
+       #print STDERR "Unit test running\n";
+       #der_test chr(0), "42";
+
+       der_test der_null, "0500";
+
+       # length bytes
+       der_test der_len(1), "01";
+       der_test der_len(127), "7f";
+       der_test der_len(128), "8180";
+       der_test der_len(256), "820100";
+       der_test der_len(65536), "83010000";
+
+       # bigint
+       der_test der_bigint(chr(0)), "020100";
+       der_test der_bigint(chr(128)), "020180"; # -128
+       der_test der_pos_bigint(chr(128)), "02020080"; # +128
+       der_test der_pos_bigint(chr(0).chr(0).chr(1)), "020101";
+       der_test der_pos_bigint(chr(0)), "020100";
+
+       # integers (tests base256 conversion)
+       der_test der_int(     0), "020100";
+       der_test der_int(   127), "02017f";
+       der_test der_int(   128), "02020080";
+       der_test der_int(   256), "02020100";
+       der_test der_int(    -1), "0201ff";
+       der_test der_int(  -128), "020180";
+       der_test der_int(  -129), "0202ff7f";
+       der_test der_int(-65536), "0203ff0000";
+       der_test der_int(-65537), "0203feffff";
+
+       # object encoding, "RSA Security"
+       der_test der_base128(840), "8648";
+       der_test der_objectid(1, 2, 840, 113549), "06062a864886f70d";
+
+       # Combinations
+       der_test der_bitstring("ABCD"), "03050041424344";
+       der_test der_bitstring(der_null), "0303000500";
+       der_test der_seq(der_int(0), der_null), "30050201000500";
+
+       # The big picture
+       der_test der_seq(der_seq(der_objectid(1, 2, 840, 113549), der_null),
+                        der_bitstring(der_seq(der_pos_bigint(chr(5)),
+                                              der_pos_bigint(chr(3))))),
+                "3017300a06062a864886f70d05000309003006020105020103";
+}
+
+####################################################
+# OpenSSL missing functionality workarounds
+
+## Format of an RSA public key:
+#    0:d=0  hl=3 l= 159 cons: SEQUENCE          
+#    3:d=1  hl=2 l=  13 cons:  SEQUENCE          
+#    5:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
+#   16:d=2  hl=2 l=   0 prim:   NULL              
+#   18:d=1  hl=3 l= 141 prim:  BIT STRING        
+#                              [ sequence: INTEGER (n), INTEGER (e) ]
+
+# generate RSA pub key in PEM format
+# $1: filename where PEM key is to be stored
+# $2: n of the RSA key in hex
+# $3: e of the RSA key in hex
+# return: nothing, but file created
+sub gen_pubrsakey($$$) {
+       my $filename=shift;
+       my $n = shift;
+       my $e = shift;
+
+       # make sure the DER encoder works ;-)
+       der_unit_test();
+
+       # generate DER encoding of the public key
+
+       my $rsaEncryption = der_objectid(1, 2, 840, 113549, 1, 1, 1);
+
+       my $der = der_seq(der_seq($rsaEncryption, der_null),
+                         der_bitstring(der_seq(der_pos_bigint(hex2bin($n)),
+                                               der_pos_bigint(hex2bin($e)))));
+
+       open(FH, ">", $filename) or die;
+       print FH pem_cert($der);
+       close FH;
+
+}
+
+# generate RSA pub key in PEM format
+#
+# This implementation uses "openssl asn1parse -genconf" which was added
+# in openssl 0.9.8. It is not available in older openssl versions.
+#
+# $1: filename where PEM key is to be stored
+# $2: n of the RSA key in hex
+# $3: e of the RSA key in hex
+# return: nothing, but file created
+sub gen_pubrsakey_using_openssl($$$) {
+       my $filename=shift;
+       my $n = shift;
+       my $e = shift;
+
+       my $asn1 = "asn1=SEQUENCE:pubkeyinfo
+
+[pubkeyinfo]
+algorithm=SEQUENCE:rsa_alg
+pubkey=BITWRAP,SEQUENCE:rsapubkey
+
+[rsa_alg]
+algorithm=OID:rsaEncryption
+parameter=NULL
+
+[rsapubkey]
+n=INTEGER:0x$n
+
+e=INTEGER:0x$e";
+
+       open(FH, ">$filename.cnf") or die "Cannot create file $filename.cnf: $?";
+       print FH $asn1;
+       close FH;
+       my @args = ("openssl", "asn1parse", "-genconf", "$filename.cnf", "-noout", "-out", "$filename.der");
+       system(@args) == 0 or die "system @args failed: $?";
+       @args = ("openssl", "rsa", "-inform", "DER", "-in", "$filename.der",
+                "-outform", "PEM", "-pubin", "-pubout", "-out", "$filename");
+       system(@args) == 0 or die "system @args failed: $?";
+       die "RSA PEM formatted key file $filename was not created"
+               if (! -f $filename);
+
+       unlink("$filename.cnf");
+       unlink("$filename.der");
+}
+
+############################################
+# Test cases
+
+# This is the Known Answer Test
+# $1: the string that we have to put in front of the key
+#     when printing the key
+# $2: crypto key1 in hex form
+# $3: crypto key2 in hex form (TDES, undef otherwise)
+# $4: crypto key3 in hex form (TDES, undef otherwise)
+# $5: IV in hex form
+# $6: Plaintext (enc=1) or Ciphertext (enc=0) in hex form
+# $7: cipher
+# $8: encrypt=1/decrypt=0
+# return: string formatted as expected by CAVS
+sub kat($$$$$$$$) {
+       my $keytype = shift;
+       my $key1 = shift;
+       my $key2 = shift;
+       my $key3 = shift;
+       my $iv = shift;
+       my $pt = shift;
+       my $cipher = shift;
+       my $enc = shift;
+
+       my $out = "";
+
+       # this is the concardination of the keys for 3DES
+       # as used in OpenSSL
+       if (defined($key2)) {
+               $out .= "$keytype = $key1\n";
+               $out .= "KEY2 = $key2\n";
+               $key1 = $key1 . $key2;
+       } else {
+               $out .= "$keytype = $key1\n";
+       }
+       if (defined($key3)) {
+               $out .= "KEY3 = $key3\n";
+               $key1= $key1 . $key3;
+       }
+       
+       $out .= "IV = $iv\n";
+       if ($enc) {
+               $out .= "PLAINTEXT = $pt\n";
+               $out .= "CIPHERTEXT = " . encrypt($key1, $iv, $cipher, $pt) . "\n";
+       } else {
+               $out .= "CIPHERTEXT = $pt\n";
+               $out .= "PLAINTEXT = " . decrypt($key1, $iv, $cipher, $pt) . "\n";
+       }
+
+       return $out;
+}
+
+# This is the Known Answer Test for Hashes
+# $1: Plaintext in hex form
+# $2: hash
+# $3: hash length (undef if not applicable)
+# return: string formatted as expected by CAVS
+sub hash_kat($$$) {
+       my $pt = shift;
+       my $cipher = shift;
+       my $len = shift;
+
+       my $out = "";
+       $out .= "Len = $len\n" if (defined($len));
+       $out .= "Msg = $pt\n";
+       $out .= "MD = " . &$hash($pt, $cipher);
+       return $out;
+}
+
+# Cipher Monte Carlo Testing
+# $1: the string that we have to put in front of the key
+#     when printing the key
+# $2: crypto key1 in hex form
+# $3: crypto key2 in hex form (TDES, undef otherwise)
+# $4: crypto key3 in hex form (TDES, undef otherwise)
+# $5: IV in hex form
+# $6: Plaintext (enc=1) or Ciphertext (enc=0) in hex form
+# $7: cipher
+# $8: encrypt=1/decrypt=0
+# return: string formatted as expected by CAVS
+sub crypto_mct($$$$$$$$) {
+       my $keytype = shift;
+        my $key1 = hex2bin(shift);
+        my $key2 = shift;
+        my $key3 = shift;
+        my $iv = hex2bin(shift);
+        my $source_data = hex2bin(shift);
+       my $cipher = shift;
+        my $enc = shift;
+
+       my $out = "";
+
+       $key2 = hex2bin($key2) if (defined($key2));
+       $key3 = hex2bin($key3) if (defined($key3));
+        my $bufsize = length($source_data);
+
+       # for AES: outer loop 0-99, inner 0-999 based on FIPS compliance tests
+       # for RC4: outer loop 0-99, inner 0-999 based on atsec compliance tests
+       # for DES: outer loop 0-399, inner 0-9999 based on FIPS compliance tests
+       my $ciph = substr($cipher,0,3);
+       my $oloop=100;
+       my $iloop=1000;
+       if ($ciph =~ /des/) {$oloop=400;$iloop=10000;}
+
+        for (my $i=0; $i<$oloop; ++$i) {
+               $out .= "COUNT = $i\n";
+               if (defined($key2)) {
+                       $out .= "$keytype = ". bin2hex($key1). "\n";
+                       $out .= "KEY2 = ". bin2hex($key2). "\n";
+                       $key1 = $key1 . $key2;
+               } else {
+                       $out .= "$keytype = ". bin2hex($key1). "\n";
+               }
+               if(defined($key3)) {
+                       $out .= "KEY3 = ". bin2hex($key3). "\n";
+                       $key1 = $key1 . $key3;
+               }
+               my $keylen = length($key1);
+
+                $out .= "IV = ". bin2hex($iv). "\n";
+
+                if ($enc) {
+                        $out .= "PLAINTEXT = ". bin2hex($source_data). "\n";
+                } else {
+                        $out .= "CIPHERTEXT = ". bin2hex($source_data). "\n";
+                }
+                my ($CO, $CI);
+               my $cipher_imp = &$state_cipher($cipher, $enc, $bufsize, $key1, $iv);
+                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;
+
+                if ($enc) {
+                        $out .= "CIPHERTEXT = ". bin2hex($calc_data). "\n\n";
+                } else {
+                        $out .= "PLAINTEXT = ". bin2hex($calc_data). "\n\n";
+                }
+               
+               if ( $ciph =~ /aes/ ) {
+                       $key1 ^= substr($old_calc_data . $calc_data, -$keylen);
+                       #print STDERR bin2hex($key1)."\n";
+               } elsif ( $ciph =~ /des/ ) {
+                       die "Wrong keylen $keylen" if ($keylen != 24);
+
+                       # $nkey needed as $key holds the concatenation of the
+                       # old key atm
+                       my $nkey = fix_key_parity(substr($key1,0,8) ^ $calc_data);
+                       #print STDERR "KEY1 = ". bin2hex($nkey)."\n";
+                       if (substr($key1,0,8) ne substr($key1,8,8)) {
+                               #print STDERR "KEY2 recalc: KEY1==KEY3, KEY2 indep. or all KEYs are indep.\n";
+                               $key2 = fix_key_parity((substr($key1,8,8) ^ $old_calc_data));
+                       } else {
+                               #print STDERR "KEY2 recalc: KEY1==KEY2==KEY3\n";
+                               $key2 = fix_key_parity((substr($key1,8,8) ^ $calc_data));
+                       }
+                       #print STDERR "KEY2 = ". bin2hex($key2)."\n";
+                       if ( substr($key1,0,8) eq substr($key1,16)) {
+                               #print STDERR "KEY3 recalc: KEY1==KEY2==KEY3 or KEY1==KEY3, KEY2 indep.\n";
+                               $key3 = fix_key_parity((substr($key1,16) ^ $calc_data));
+                       } else {
+                               #print STDERR "KEY3 recalc: all KEYs are independent\n";
+                               $key3 = fix_key_parity((substr($key1,16) ^ $old_old_calc_data));
+                       }
+                       #print STDERR "KEY3 = ". bin2hex($key3)."\n";
+
+                       # reset the first key - concardination happens at
+                       # beginning of loop
+                       $key1=$nkey;
+               } elsif ($ciph =~ /rc4/ ) {
+                       $key1 ^= substr($calc_data, 0, 16);
+                       #print STDERR bin2hex($key1)."\n";
+               } else {
+                       die "Test limitation: cipher '$cipher' not supported in Monte Carlo testing";
+               }
+
+               if (! $enc && $ciph =~ /des/ ) {
+                       #TDES in decryption mode has a special rule
+                       $iv = $old_calc_data;
+                       $source_data = $calc_data;
+               } elsif ( $ciph =~ /rc4/ ) {
+                       #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;
+               }
+        }
+
+       return $out;
+}
+
+# Hash Monte Carlo Testing
+# $1: Plaintext in hex form
+# $2: hash
+# return: string formatted as expected by CAVS
+sub hash_mct($$) {
+       my $pt = shift;
+       my $cipher = shift;
+
+       my $out = "";
+
+       $out .= "Seed = $pt\n\n";
+       
+        for (my $j=0; $j<100; ++$j) {
+               $out .= "COUNT = $j\n";
+               my $md0=$pt;
+               my $md1=$pt;
+               my $md2=$pt;
+               for (my $i=0; $i<1000; ++$i) {
+                       my $mi= $md0 . $md1 . $md2;
+                       $md0=$md1;
+                       $md1=$md2;
+                       $md2 = &$hash($mi, $cipher);
+                       $md2 =~ s/\n//;
+               }
+                $out .= "MD = $md2\n\n";
+               $pt=$md2;
+       }
+
+       return $out;
+}
+
+# RSA SigGen test
+# $1: Message to be signed in hex form
+# $2: Hash algorithm
+# $3: file name with RSA key in PEM form
+# return: string formatted as expected by CAVS
+sub rsa_siggen($$$) {
+       my $data = shift;
+       my $cipher = shift;
+       my $keyfile = shift;
+
+       my $out = "";
+       
+       $out .= "SHAAlg = $cipher\n";
+       $out .= "Msg = $data\n";
+       $out .= "S = " . &$rsa_sign($data, $cipher, $keyfile) . "\n";
+
+       return $out;
+}
+
+# RSA SigVer test
+# $1: Message to be verified in hex form
+# $2: Hash algoritm
+# $3: Signature of message in hex form
+# $4: n of the RSA key in hex in hex form
+# $5: e of the RSA key in hex in hex form
+# return: string formatted as expected by CAVS
+sub rsa_sigver($$$$$) {
+       my $data = shift;
+       my $cipher = shift;
+       my $signature = shift;
+       my $n = shift;
+       my $e = shift;
+
+       my $out = "";
+
+       $out .= "SHAAlg = $cipher\n";
+       $out .= "e = $e\n";
+       $out .= "Msg = $data\n";
+       $out .= "S = $signature\n";
+
+       # XXX maybe a secure temp file name is better here
+       # but since it is not run on a security sensitive
+       # system, I hope that this is fine
+       my $keyfile = "rsa_sigver.tmp.$$";
+       gen_pubrsakey($keyfile, $n, $e);
+
+       my $sigfile = "$keyfile.sig";
+       open(FH, ">$sigfile") or die "Cannot create file $sigfile: $?";
+       print FH hex2bin($signature);
+       close FH;
+
+       $out .= "Result = " . (&$rsa_verify($data, $cipher, $keyfile, $sigfile) ? "P\n" : "F\n");
+
+       unlink($keyfile);
+       unlink($sigfile);
+
+       return $out;
+}
+
+# X9.31 RNG test
+# $1 key for the AES cipher
+# $2 DT value
+# $3 V value
+# $4 type ("VST", "MCT")
+# return: string formatted as expected by CAVS
+sub rngx931($$$$) {
+       my $key=shift;
+       my $dt=shift;
+       my $v=shift;
+       my $type=shift;
+
+       my $out = "Key = $key\n";
+       $out   .= "DT = $dt\n";
+       $out   .= "V = $v\n";
+
+       my $count = 1;
+       $count = 10000 if ($type eq "MCT");
+
+       my $rnd_val = "";
+
+       # we read 16 bytes from RNG
+       my $bufsize = 16;
+
+       my ($CO, $CI);
+       my $rng_imp = &$state_rng($key, $dt, $v);
+       my $pid = open2($CO, $CI, $rng_imp);
+       for (my $i = 0; $i < $count; ++$i) {
+               my $len = sysread $CO, $rnd_val, $bufsize;
+               #print STDERR "len=$len, bufsize=$bufsize\n";
+               die "len=$len != bufsize=$bufsize" if $len ne $bufsize;
+               #print STDERR "calc_data=", bin2hex($rnd_val), "\n";
+       }
+       close $CO;
+       close $CI;
+       waitpid $pid, 0;
+
+       $out .= "R = " . bin2hex($rnd_val) . "\n\n";
+
+       return $out;
+}
+
+##############################################################
+# Parser of input file and generator of result file
+#
+
+sub usage() {
+
+       print STDERR "Usage:
+$0 [-R] <CAVS-test vector file>
+
+-R  execution of ARCFOUR instead of OpenSSL";
+
+}
+
+# Parser of CAVS test vector file
+# $1: Test vector file
+# $2: Output file for test results
+# return: nothing
+sub parse($$) {
+       my $infile = shift;
+       my $outfile = shift;
+
+       my $out = "";
+
+       # Do I need to generate the key?
+       my $rsa_keygen = 0;
+
+       # this is my cipher/hash type
+       my $cipher = "";
+
+       # Test type
+       # 1 - cipher known answer test
+       # 2 - cipher Monte Carlo test
+       # 3 - hash known answer test
+       # 4 - hash Monte Carlo test
+       # 5 - RSA signature generation
+       # 6 - RSA signature verification
+       my $tt = 0;
+
+       # Variables for tests
+       my $keytype = ""; # we can have "KEY", "KEYs", "KEY1"
+       my $key1 = "";
+       my $key2 = undef; #undef needed for allowing
+       my $key3 = undef; #the use of them as input variables
+       my $pt = "";
+       my $enc = 1;
+       my $iv = "";
+       my $len = undef; #see key2|3
+       my $n = "";
+       my $e = "";
+       my $signature = "";
+       my $rsa_keyfile = "";
+       my $dt = "";
+       my $v = "";
+
+       my $mode = "";
+
+       open(IN, "<$infile");
+       while(<IN>) {
+
+               my $line = $_;
+               chomp($line);
+               $line =~ s/\r//;
+
+               my $keylen = "";
+
+               # Mode and type check
+               # consider the following parsed line
+               # '# AESVS MCT test data for CBC'
+               # '# TDES Multi block Message Test for CBC'
+               # '# INVERSE PERMUTATION - KAT for CBC'
+               # '# SUBSTITUTION TABLE - KAT for CBC'
+               # '# TDES Monte Carlo (Modes) Test for CBC'
+               # '#  "SHA-1 Monte" information for "IBMRHEL5"'
+               # '# "SigVer PKCS#1 Ver 1.5" information for "IBMRHEL5"'
+               # '# "SigGen PKCS#1 Ver 1.5" information for "IBMRHEL5"'
+               # '#RC4VS MCT test data'
+               
+               # avoid false positives from user specified 'for "PRODUCT"' strings
+               my $tmpline = $line;
+               $tmpline =~ s/ for ".*"//;
+
+               ##### Extract cipher
+               # XXX there may be more - to be added
+               if ($tmpline =~ /^#.*(CBC|ECB|OFB|CFB|SHA-1|SigGen|SigVer|RC4VS|ANSI X9\.31)/) {
+                       if ($tmpline    =~ /CBC/)   { $mode="cbc"; }
+                       elsif ($tmpline =~ /ECB/)   { $mode="ecb"; }
+                       elsif ($tmpline =~ /OFB/)   { $mode="ofb"; }
+                       elsif ($tmpline =~ /CFB/)   { $mode="cfb"; }
+                       #we do not need mode as the cipher is already clear
+                       elsif ($tmpline =~ /SHA-1/) { $cipher="sha1"; }
+                       #we do not need mode as the cipher is already clear
+                       elsif ($tmpline =~ /RC4VS/) { $cipher="rc4"; }
+                       elsif ($tmpline =~ /SigGen|SigVer/) {
+                               die "Error: X9.31 is not supported"
+                                       if ($tmpline =~ /X9/);
+                               $cipher="sha1"; #place holder - might be overwritten later
+                       }
+
+                       # RSA Key Generation test
+                       if ($tmpline =~ /SigGen/) {
+                               $rsa_keygen = 1;
+                       }
+                       if ($tmpline =~ /^#.*AESVS/) {
+                               # AES cipher (part of it)
+                               $cipher="aes";
+                       }
+                       if ($tmpline =~ /^#.*(TDES|KAT)/) {
+                               # TDES cipher (full definition)
+                               # the FIPS-140 test generator tool does not produce
+                               # machine readable output!
+                               if ($mode eq "cbc") { $cipher="des-ede3-cbc"; }
+                               if ($mode eq "ecb") { $cipher="des-ede3"; }
+                               if ($mode eq "ofb") { $cipher="des-ede3-ofb"; }
+                               if ($mode eq "cfb") { $cipher="des-ede3-cfb"; }
+                       }
+
+                       # check for RNG
+                       if ($tmpline =~ /ANSI X9\.31/) {
+                               # change the tmpline to add the type of the
+                               # test which is ONLY visible from the file
+                               # name :-(
+                               if ($infile =~ /MCT\.req/) {
+                                       $tmpline .= " MCT";
+                               } elsif ($infile =~ /VST\.req/) {
+                                       $tmpline .= " VST";
+                               } else {
+                                       die "Unexpected cipher type with $infile";
+                               }
+                       }
+
+
+                       ##### Identify the test type
+                       if ($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 "sha1") {
+                               $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 eq "sha1" && $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));
+                       }
+               }
+
+               # This is needed as ARCFOUR does not operate with an IV
+               $iv = "00000000000000000000000000000000" if ($cipher eq "rc4"
+                                                            && $iv eq "" );
+
+               # we are now looking for the string
+               # '# Key Length : 256'
+               # found in AES
+               if ($tmpline =~ /^# Key Length.*?(128|192|256)/) {
+                       if ($cipher eq "aes") {
+                               $cipher="$cipher-$1-$mode";
+                       } else {
+                               die "Error: Key length $1 given for cipher $cipher which is unexpected";
+                       }
+               }
+
+               # Get the test data
+               if ($line =~ /^(KEY|KEYs|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 =~ /^KEY2\s*=\s*(.*)/) { # found in TDES
+                       die "First key not set, but got already second key - input file crap" if ($key1 eq "");
+                       die "KEY2 seen twice - input file crap" if (defined($key2));
+                       $key2=$1;
+                       $key2 =~ s/\s//g; #replace potential white spaces
+               }
+               elsif ($line =~ /^KEY3\s*=\s*(.*)/) { # found in TDES
+                       die "Second key not set, but got already third key - input file crap" if ($key2 eq "");
+                       die "KEY2 seen twice - input file crap" if (defined($key3));
+                       $key3=$1;
+                       $key3 =~ s/\s//g; #replace potential white spaces
+               }
+               elsif ($line =~ /^IV\s*=\s*(.*)/) { # found in ciphers
+                       die "IV seen twice - input file crap" if ($iv ne "");
+                       $iv=$1;
+                       $iv =~ s/\s//g; #replace potential white spaces
+               }
+               elsif ($line =~ /^PLAINTEXT\s*=\s*(.*)/) { # found in ciphers
+                       if ( $1 !~ /\?/ ) { #only use it if there is valid hex data
+                               die "PLAINTEXT/CIPHERTEXT seen twice - input file crap" if ($pt ne "");
+                               $pt=$1;
+                               $pt =~ s/\s//g; #replace potential white spaces
+                               $enc=1;
+                       }
+               }
+               elsif ($line =~ /^CIPHERTEXT\s*=\s*(.*)/) { # found in ciphers
+                       if ( $1 !~ /\?/ ) { #only use it if there is valid hex data
+                               die "PLAINTEXT/CIPHERTEXT seen twice - input file crap" if ($pt ne "");
+                               $pt=$1;
+                               $pt =~ s/\s//g; #replace potential white spaces
+                               $enc=0;
+                       }
+               }
+               elsif ($line =~ /^Len\s*=\s*(.*)/) { # found in hashs
+                       $len=$1;
+               }
+               elsif ($line =~ /^(Msg|Seed)\s*=\s*(.*)/) { # found in hashs
+                       die "Msg/Seed seen twice - input file crap" if ($pt ne "");
+                       $pt=$2;
+               }
+               elsif ($line =~ /^\[mod\s*=\s*(.*)\]$/) { # found in RSA requests
+                       $out .= $line . "\n"; # print it
+                       # generate the private key with given bit length now
+                       # as we have the required key length in bit
+                       if ($tt == 5) {
+                               # XXX maybe a secure temp file name is better here
+                               # but since it is not run on a security sensitive
+                               # system, I hope that this is fine
+                               $rsa_keyfile = "rsa_siggen.tmp.$$";
+                               &$gen_rsakey($1, $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"
+                       }
+               }
+               elsif ($line =~ /^SHAAlg\s*=\s*(.*)/) { #found in RSA requests
+                       $cipher=$1;
+               }
+               elsif($line =~ /^n\s*=\s*(.*)/) { # found in RSA requests
+                       $out .= $line . "\n";
+                       $n=$1;
+               }
+               elsif ($line =~ /^e\s*=\s*(.*)/) { # found in RSA requests
+                       $e=$1;
+               }
+               elsif ($line =~ /^S\s*=\s*(.*)/) { # found in RSA requests
+                       die "S seen twice - input file crap" if ($signature ne "");
+                       $signature=$1;
+               }
+               elsif ($line =~ /^DT\s*=\s*(.*)/) { # X9.31 RNG requests
+                       die "DT seen twice - check input file"
+                               if ($dt ne "");
+                       $dt=$1;
+               }
+               elsif ($line =~ /^V\s*=\s*(.*)/) { # X9.31 RNG requests
+                       die "V seen twice - check input file"
+                               if ($v ne "");
+                       $v=$1;
+               }
+               else {
+                       $out .= $line . "\n";
+               }
+
+               # call tests if all input data is there
+               if ($tt == 1) {
+                       if ($key1 ne "" && $iv ne "" && $pt ne "" && $cipher ne "") {
+                               $out .= kat($keytype, $key1, $key2, $key3, $iv, $pt, $cipher, $enc);
+                               $keytype = "";
+                               $key1 = "";
+                               $key2 = undef;
+                               $key3 = undef;
+                               $iv = "";
+                               $pt = "";
+                       }
+               }
+               elsif ($tt == 2) {
+                       if ($key1 ne "" && $iv ne "" && $pt ne "" && $cipher ne "") {
+                               $out .= crypto_mct($keytype, $key1, $key2, $key3, $iv, $pt, $cipher, $enc);
+                               $keytype = "";
+                               $key1 = "";
+                               $key2 = undef;
+                               $key3 = undef;
+                               $iv = "";
+                               $pt = "";
+                       }
+               }
+               elsif ($tt == 3) {
+                       if ($pt ne "" && $cipher ne "") {
+                               $out .= hash_kat($pt, $cipher, $len);
+                               $pt = "";
+                               $len = undef;
+                       }
+               }
+               elsif ($tt == 4) {
+                       if ($pt ne "" && $cipher ne "") {
+                               $out .= hash_mct($pt, $cipher);
+                               $pt = "";
+                       }
+               }
+               elsif ($tt == 5) {
+                       if ($pt ne "" && $cipher ne "" && $rsa_keyfile ne "") {
+                               $out .= rsa_siggen($pt, $cipher, $rsa_keyfile);
+                               $pt = "";
+                       }
+               }
+               elsif ($tt == 6) {
+                       if ($pt ne "" && $cipher ne "" && $signature ne "" && $n ne "" && $e ne "") {
+                               $out .= rsa_sigver($pt, $cipher, $signature, $n, $e);
+                               $pt = "";
+                               $signature = "";
+                       }
+               }
+               elsif ($tt == 7 ) {
+                       if ($key1 ne "" && $dt ne "" && $v ne "") {
+                               $out .= rngx931($key1, $dt, $v, "VST");
+                               $key1 = "";
+                               $dt = "";
+                               $v = "";
+                       }
+               }
+               elsif ($tt == 8 ) {
+                       if ($key1 ne "" && $dt ne "" && $v ne "") {
+                               $out .= rngx931($key1, $dt, $v, "MCT");
+                               $key1 = "";
+                               $dt = "";
+                               $v = "";
+                       }
+               }
+               elsif ($tt > 0) {
+                       die "Test case $tt not defined";
+               }
+       }
+
+       close IN;
+       $out =~ s/\n/\r\n/g; # make it a dos file
+       open(OUT, ">$outfile") or die "Cannot create output file $outfile: $?";
+       print OUT $out;
+       close OUT;
+
+}
+
+# Signalhandler
+sub cleanup() {
+       unlink("rsa_siggen.tmp.$$");
+       unlink("rsa_sigver.tmp.$$");
+       unlink("rsa_sigver.tmp.$$.sig");
+       unlink("rsa_sigver.tmp.$$.der");
+       unlink("rsa_sigver.tmp.$$.cnf");
+       exit;
+}
+
+############################################################
+#
+# let us pretend to be C :-)
+sub main() {
+
+       usage() unless @ARGV;
+
+       getopts("R", \%opt) or die "bad option";
+
+       ##### Set library
+
+       #print STDERR "Using OpenSSL interface functions\n";
+       #$encdec=\&openssl_encdec;
+       #$rsa_sign=\&openssl_rsa_sign;
+       #$rsa_verify=\&openssl_rsa_verify;
+       #$gen_rsakey=\&openssl_gen_rsakey;
+       #$hash=\&openssl_hash;
+       #$state_cipher=\&openssl_state_cipher;
+
+       print STDERR "Using libgcrypt interface functions\n";
+       $state_rng=\&libgcrypt_state_rng;
+
+       my $infile=$ARGV[0];
+       die "Error: Test vector file $infile not found" if (! -f $infile);
+
+       my $outfile = $infile;
+       # let us add .rsp regardless whether we could strip .req
+       $outfile =~ s/\.req$//;
+       if ($opt{'R'}) {
+               $outfile .= ".rc4";
+       } else {
+               $outfile .= ".rsp";
+       }
+       if (-f $outfile) {
+               die "Output file $outfile could not be removed: $?"
+                       unless unlink($outfile);
+       }
+       print STDERR "Performing tests from source file $infile with results stored in destination file $outfile\n";
+
+       #Signal handler
+       $SIG{HUP} = \&cleanup;
+       $SIG{INT} = \&cleanup;
+       $SIG{QUIT} = \&cleanup;
+       $SIG{TERM} = \&cleanup;
+
+       # Do the job
+       parse($infile, $outfile);
+
+       unlink("rsa_siggen.tmp.$$");
+
+}
+
+###########################################
+# Call it
+main();
+1;
index dfcea7c..d10c9ca 100644 (file)
@@ -209,7 +209,9 @@ main (int argc, char **argv)
   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
 
-  err = init_external_test (&context, 0, key, 16, seed, 16, dt, 16);
+  /* The flag value 1 disables the dup check, so that the RNG returns
+     all generated data.  */
+  err = init_external_test (&context, 1, key, 16, seed, 16, dt, 16);
   if (err)
     die ("init external test failed: %s\n", gpg_strerror (err));