diff options
-rw-r--r-- | cipher/ChangeLog | 4 | ||||
-rw-r--r-- | cipher/ecc.c | 4 | ||||
-rw-r--r-- | doc/gcrypt.texi | 6 | ||||
-rw-r--r-- | random/ChangeLog | 5 | ||||
-rw-r--r-- | random/rand-internal.h | 3 | ||||
-rw-r--r-- | random/random-fips.c | 50 | ||||
-rw-r--r-- | random/random.c | 2 | ||||
-rw-r--r-- | src/ChangeLog | 2 | ||||
-rw-r--r-- | src/global.c | 5 | ||||
-rw-r--r-- | tests/ChangeLog | 1 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/cavs_driver.pl | 1498 | ||||
-rw-r--r-- | tests/fipsrngdrv.c | 4 |
13 files changed, 1563 insertions, 24 deletions
diff --git a/cipher/ChangeLog b/cipher/ChangeLog index 25a9cd3d..502fb4c6 100644 --- a/cipher/ChangeLog +++ b/cipher/ChangeLog @@ -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. diff --git a/cipher/ecc.c b/cipher/ecc.c index 5d5c322e..c90391f9 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -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: diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index 1608d6c9..e5f3de0a 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -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. diff --git a/random/ChangeLog b/random/ChangeLog index c22ea2ea..1b6c4688 100644 --- a/random/ChangeLog +++ b/random/ChangeLog @@ -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> diff --git a/random/rand-internal.h b/random/rand-internal.h index 269fad95..534d8284 100644 --- a/random/rand-internal.h +++ b/random/rand-internal.h @@ -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, diff --git a/random/random-fips.c b/random/random-fips.c index 2fc9596e..2667e71f 100644 --- a/random/random-fips.c +++ b/random/random-fips.c @@ -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; diff --git a/random/random.c b/random/random.c index 9da83cf0..85d5fa97 100644 --- a/random/random.c +++ b/random/random.c @@ -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 diff --git a/src/ChangeLog b/src/ChangeLog index 04327cb0..b96f8bb7 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -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. diff --git a/src/global.c b/src/global.c index ce54f0d3..4af7412a 100644 --- a/src/global.c +++ b/src/global.c @@ -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' ); } diff --git a/tests/ChangeLog b/tests/ChangeLog index c24fb5b1..de5381d4 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -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> diff --git a/tests/Makefile.am b/tests/Makefile.am index 509647a5..21d5bd84 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..3ebd2e3d --- /dev/null +++ b/tests/cavs_driver.pl @@ -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; diff --git a/tests/fipsrngdrv.c b/tests/fipsrngdrv.c index dfcea7c1..d10c9cac 100644 --- a/tests/fipsrngdrv.c +++ b/tests/fipsrngdrv.c @@ -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)); |