diff --git a/tests/ChangeLog b/tests/ChangeLog index 46331152..ff5d0b45 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,637 +1,650 @@ +2008-10-06 Werner Koch + + * cavs_driver.pl: New version from upstream. + (libgcrypt_rsa_verify($$$$)): Pass pkcs1. + (libgcrypt_rsa_sign($$$)): Pass pkcs1 and hash algo. + + * fipsdrv.c (run_rsa_sign): Hash data in pkcs1 mode. + (run_rsa_verify): Ditto. + (read_key_file): Rename to read_private_key_file. Factor public + key code out to.. + (read_public_key_file): .. new. + 2008-10-02 Werner Koch * fipsdrv.c (print_buffer): Add base64 printing code. (base64_decode, read_key_file, parse_tag, read_sig_file): New. (run_rsa_gen, run_rsa_sign): New. (main): Add modes rsa-gen, rsa-sign and rsa-verify. + 2008-09-29 Werner Koch * fipsdrv.c: Merge code from fipsrngdrv.c * fipsrngdrv.c: Remove. 2008-09-26 Werner Koch * Makefile.am: Distribute cavs_driver.pl. * cavs_tests.sh: New. * fipsdrv.c: New. 2008-09-18 Werner Koch * benchmark.c (main): Do not disable secure memory in FIPS mode. 2008-09-18 Werner Koch * basic.c (main): Do not disable secure memory in FIPS mode. 2008-09-16 Werner Koch * fipsrngdrv.c (main): Bail out on write error. Implement verbose option. (main): Use flag to disable dup block checks. 2008-09-15 Werner Koch * fipsrngdrv.c: New. 2008-09-09 Werner Koch * basic.c (main): New option --selftest. 2008-08-29 Werner Koch * keygrip.c: Update to also check ECDSA. 2008-08-28 Werner Koch * rsa-16k.key: New sample key. 2008-08-27 Werner Koch * pkbench.c (read_file): New. (process_key_pair_file): Replace mmap by read_file. (main): Add a --fips option. * Makefile.am (EXTRA_DIST): Remove. (EXTRA_PROGRAMS): Add pkbench. * basic.c (main): Extended FIPS self-test test. 2008-08-26 Werner Koch * basic.c (get_keys_new): Use transient-key flag. * benchmark.c (main): First check options then do the libgcrypt initialization. (rsa_bench): Use transient-key flag if not in fips mode. 2008-08-20 Werner Koch * t-mpi-bit.c (test_lshift): New. (mpi2bitstr_nlz, lshiftbitstring): New. (main): Run test. 2008-08-18 Werner Koch * basic.c (main): Add option --fips. 2008-08-15 Werner Koch * register.c (main): Check for fips mode. (check_run): Take care of fips mode. * basic.c (check_cbc_mac_cipher, check_ciphers, check_digests) (check_hmac, check_pubkey): Do not test unavalaible algorithms in fips mode. (main): Check for fips mode. 2008-04-22 Werner Koch * basic.c (check_one_cipher): Also check in-place encryption. 2008-03-17 Werner Koch * benchmark.c (main): Add option --cipher-repetition. (cipher_bench): Use it. 2008-03-12 Werner Koch * benchmark.c (rsa_bench): Add arg NO_BLINDING. (main): Add option --no-blinding. 2007-12-05 Werner Koch * pubkey.c (sample_private_key_1_1,sample_private_key_1_2): New. (get_keys_sample): Add arg SECRET_VARIANT. (check_run): Check all variants. Also check gcry_pk_testkey. (check_keys_crypt): Add DECRYPT_FAIL_CODE. (check_keys): Ditto. 2007-11-30 Werner Koch * benchmark.c (main): Add optione --verbose and reworked the option parsing. (random_bench): Dump random stats. 2007-10-31 Werner Koch * benchmark.c (start_timer, stop_timer, elapsed_time) [W32]: Fixed. 2007-06-20 Werner Koch * benchmark.c (rsa_bench): New. (main): New command "rsa". 2007-05-03 Werner Koch * Makefile.am (EXTRA_DIST): Do not build pkbench.c 2007-05-02 David Shaw * basic.c (check_ciphers): Add Camellia. 2007-04-30 David Shaw * basic.c (check_ciphers): #if out ciphers we don't have. Add test for GCRY_CIPHER_RFC2268_40. 2007-04-30 Werner Koch * version.c: New. * Makefile.am (TESTS): Add version. 2007-04-30 Marcus Brinkmann * benchmark.c (ecc_bench): Release KEY_SPEC. 2007-04-28 Marcus Brinkmann * ac-data.c (check_run): Don't give redundant GCRY_AC_FLAG_DEALLOC in addition to GCRY_AC_FLAG_COPY. Don't release LABEL1 or MPI0, as those are donated to libgcrypt, but do release MPI0 and MPI2. 2007-04-12 Marcus Brinkmann * ac-schemes.c (scheme_spec): Revert last change. * ac-schemes.c (scheme_spec): Remove const qualifier from member M. (es_check): Remove const qualifier from C and M2. 2007-03-28 Werner Koch * pkbench.c (generate_key): Support named curves. * benchmark.c (dsa_bench): New args ITERATIONS and PRINT_HEADER. (main): Call dsa and ecc benchs. (show_sexp): New. * Makefile.am (TESTS): Move pkbench to EXTRA_PROGRAMS. 2007-03-22 Werner Koch * benchmark.c (die): New. (ecc_bench): New. * pkbench.c (main): Reworked to provide proper option handling. 2007-03-13 Werner Koch * mpitests.c: Reformatted to GNU standards. (main): Add options --verbose and --debug for future use. 2007-03-13 Werner Dittmann (wk) * mpitests.c: New. 2007-02-23 Werner Koch * Makefile.am (TEST): Run benchmark as last. * ac-data.c (check_sexp_conversion): Print label only in verbose mode. * pubkey.c (main): Run test just 2 times instead of 10. (get_elg_key_new): New. (check_run): Also run tests with Elgamal keys. (check_keys): New arg NBITS_DATA. (get_elg_key_new): Use only 400 for the 512 bit Elgamal test. * random.c: New. 2007-02-22 Werner Koch * basic.c (check_pubkey_sign): Also try signing using an OID. * Makefile.am (TESTS) [W32]: Removed pkbench for now. * pkbench.c (benchmark): Fixed for W32. 2007-02-21 Werner Koch * hmac.c (check_one_mac): Make pointer args const. * basic.c (check_one_md): Ditto. (check_one_hmac): Ditto. * keygen.c (progress_cb): Filter out line feeds. * basic.c (progress_handler): Ditto. 2006-12-18 Werner Koch * Makefile.am (AM_CFLAGS, AM_CPPFLAGS): Splitted and merged with Moritz' changes. (INCLUDES): Removed. * keygen.c (progress_handler): New. (main): Use it in verbose mode. 2006-11-05 Moritz Schulte * Makefile.am (AM_CFLAGS): Added -I$(top_builddir)/src so that the new gcrypt.h is used, not the one installed in the system. 2006-10-17 Werner Koch * keygen.c (check_rsa_keys): Also create an 1536 bit DSA key. 2006-08-03 Werner Koch * t-mpi-bit.c: New. 2006-07-06 Werner Koch * benchmark.c (main): New option --use-random-daemon. New command strongrandom. (random_bench): New arg VERY_STRONG. 2006-03-14 Werner Koch * benchmark.c (main): Allow for seed file argument to random bench. * basic.c (main): Use progress handler only in verbose mode. (main): Speed up test key generation. * ac-data.c (check_sexp_conversion, check_run): Take care of VERBOSE. * ac.c (main): Ditto. * pubkey.c (main): Ditto. * pkbench.c (main): Ditto. * keygen.c (main): Ditto. (check_rsa_keys): Print key only in verbose mode. 2006-03-10 Brad Hards (wk, patch 2006-02-18) * basic.c (check_one_hmac, check_hmac): New. 2006-03-07 Werner Koch * benchmark.c (cipher_bench): Add OFB mode. 2006-01-18 Brad Hards (wk 2006-03-07) * basic.c: Added test cases for OFB and CFB modes. Fixed some compiler warnings for signedness. 2005-11-12 Moritz Schulte * ac-data.c: Added way more test cases. 2005-09-15 Moritz Schulte * Makefile.am (TESTS): Added keygrip. * keygrip.c: New. 2005-09-19 Werner Koch * benchmark.c (dsa_bench): New. 2005-08-19 Werner Koch * hmac.c (main): Added all FIPS tests. 2005-08-18 Werner Koch * hmac.c: New. 2005-04-22 Moritz Schulte * tsexp.c: Include in case HAVE_CONFIG_H is defined; thanks to Albert Chin. * testapi.c: Likewise. * register.c: Likewise. * pubkey.c: Likewise. * prime.c: Likewise. * pkbench.c: Likewise. * keygen.c: Likewise. * benchmark.c: Likewise. * basic.c: Likewise. * ac-schemes.c: Likewise. * ac-data.c: Likewise. * ac.c: Likewise. 2005-04-16 Moritz Schulte * ac-data.c (check_run): Include new test. 2005-04-11 Moritz Schulte * basic.c (check_digests): Add tests for Whirlpool. 2005-03-30 Moritz Schulte * ac-schemes.c: New file. * ac-data.c: New file. * Makefile.am (TESTS): Added ac-schemes and ac-data. 2004-09-15 Moritz Schulte * pkbench.c: Include . 2004-08-24 Moritz Schulte * pkbench.c (context_init): Improve generation of test data. 2004-08-23 Moritz Schulte * Makefile.am (TESTS): Added: pkbench. * pkbench.c: New file. 2004-02-25 Werner Koch * Makefile.am (TEST): Add benchmark. * benchmark.c (md_bench, cipher_bench): Allow NULL arg to to run tests for all algorithms. (main): Run all tests by default. 2004-02-03 Werner Koch * tsexp.c (basic): New pass to check secure memory switching. 2004-01-12 Moritz Schulte * ac.c (check_one): Adjust to new ac API. 2003-11-22 Werner Koch * pubkey.c (check_keys_crypt): Fixed my last patch. 2003-11-11 Werner Koch * tsexp.c (basic): Add pass structure and a test for the %b format. 2003-11-04 Werner Koch * Makefile.am (noinst_PROGRAMS): Use this so that test programs get always build. * keygen.c (check_nonce): New. (main): Add a basic check for the nocen function. 2003-10-31 Werner Koch * basic.c (check_aes128_cbc_cts_cipher): Make it a prototype * ac.c (check_run): Comment unused variable. 2003-10-10 Werner Koch * prime.c (check_primes): Generate a generator and avoid printing unless in verbose mode. 2003-10-07 Werner Koch * tsexp.c (check_sscan): New. 2003-09-04 Werner Koch * pubkey.c (check_keys_crypt): Fix for compatibility mode. 2003-09-02 Moritz Schulte * Makefile.am (TESTS): Added: prime. * prime.c: New file. 2003-08-27 Moritz Schulte * basic.c (check_ciphers): Added: Serpent. Write braces around flags. 2003-08-04 Moritz Schulte * benchmark.c (do_powm): Adjust for new gcry_mpi_scan interface. 2003-07-23 Moritz Schulte * ac.c (key_copy): New function... (check_one): ... use it. 2003-07-22 Moritz Schulte * basic.c (check_ciphers): Use gcry_cipher_map_name. 2003-07-18 Moritz Schulte * ac.c (check_run): Renamed to ... (check_one): ... this, changed calling interface. (check_run): New function. * register.c: Adjust gcry_cipher_spec_t structure. 2003-07-14 Moritz Schulte * register.c: Adjust cipher specification structure. * benchmark.c: New file. * testapi.c: New file. * Makefile.am (EXTRA_PROGRAMS): Set to: benchmark testapi. (check_PROGRAMS): Set to: $(TESTS). 2003-07-12 Moritz Schulte * ac.c, basic.c, keygen.c, register.c, sexp.c, tsexp.c: Used gcry_err* wrappers for libgpg symbols. * basic.c (check_ciphers): Added: GCRY_CIPHER_TWOFISH128. 2003-07-08 Moritz Schulte * Makefile.am (LIBS): Remove: -lpthread. * basic.c (check_one_cipher): Fix variable initialization. Thanks to Simon Joseffson . 2003-07-07 Moritz Schulte * Makefile.am (TESTS): Added: register. 2003-07-05 Moritz Schulte * register.c (check_run): Adjusted for new gcry_cipher_register API. 2003-07-02 Moritz Schulte * Makefile.am (TESTS): Added: ac. * ac.c: New file. 2003-06-18 Werner Koch * basic.c (check_cbc_mac_cipher): Adjusted for new API of get_blklen and get_keylen. (check_ctr_cipher): Ditto. (check_one_cipher): Ditto. (check_one_md): Adjusted for new API of gcry_md_copy. 2003-06-18 Moritz Schulte * register.c: Replace old type GcryModule with newer one: gcry_module_t. Adjusted for new API. * Makefile.am (AM_CFLAGS): Added: @GPG_ERROR_CFLAGS@. 2003-06-15 Moritz Schulte * basic.c (get_keys_new): New function. (do_check_one_pubkey): New function ... (check_one_pubkey): ... use it. (progress_handler): New function. (main): Use gcry_set_progress_handler. 2003-06-14 Moritz Schulte * basic.c: Replaced calls to gcry_strerror with calls to gpg_strerror. (check_one_md): Adjust for new gcry_md_copy API. * tsexp.c: Likewise. * keygen.c: Likewise. 2003-06-12 Moritz Schulte * basic.c: Changed here and there, reorganized pubkey checks, added DSA and ELG keys. 2003-06-09 Moritz Schulte * basic.c, keygen.c, pubkey.c, register.c, tsexp.c: Changed to use new API. 2003-06-01 Moritz Schulte * tsexp.c (canon_len): Adjust for new gcry_sexp_canon_len API. 2003-05-26 Moritz Schulte * basic.c (verify_one_signature): Adjust for libgpg-error. (check_pubkey_sign): Likewise. (check_pubkey): Likewise. * basic.c (check_pubkey_sign): Likewise. * tsexp.c (canon_len): Likewise. (back_and_forth_one): Likewise. 2003-04-27 Moritz Schulte * pubkey.c: Changed the sample private key to contain the identifier `openpgp-rsa' instead of `rsa'. * basic.c (check_digests): Enabled/fixed some tests for TIGER. 2003-04-17 Moritz Schulte * Makefile.am (TESTS): Removed `register' for now. 2003-04-17 Moritz Schulte * basic.c (check_digests): Include checks for SHA512 and SHA384. 2003-04-16 Moritz Schulte * basic.c (check_one_md): Also test md_copy. 2003-04-07 Moritz Schulte * Makefile.am (TESTS): Added register. * register.c: New file. 2003-03-30 Simon Josefsson * basic.c (check_one_cipher): New. Test CTR. (main): Call it. (check_ciphers): Check CTR mode. 2003-03-26 Moritz Schulte * Makefile.am (TESTS): Added pubkey. * pubkey.c: New file. 2003-03-22 Simon Josefsson * basic.c (check_cbc_mac_cipher): New. (main): Use it. 2003-03-19 Werner Koch * keygen.c (check_rsa_keys): Don't expect an exponent when asking for e=0. (check_generated_rsa_key): Just print exponent if EXPECTED_E is 0. 2003-03-02 Moritz Schulte * basic.c (check_one_cipher): Use gcry_cipher_reset() instead of gcry_cipher_close(), gcry_cipher_open and gcry_cipher_setkey(). 2003-01-23 Werner Koch * keygen.c: New. 2003-01-20 Simon Josefsson * basic.c (check_digests): Add CRC. (check_one_md): Print computed and expected values on error. 2003-01-20 Werner Koch * basic.c (check_one_md): Kludge to check a one million "a". (check_digests): Add checks for SHA-256. 2003-01-20 Werner Koch * basic.c (check_pubkey): Check the keygrip for the sample key. 2003-01-15 Werner Koch * basic.c (verify_one_signature,check_pubkey_sign) (check_pubkey): New. (main): Check public key functions. Add a --debug option. 2002-11-23 Werner Koch * basic.c (check_digests): Add another test for MD4. By Simon Josefsson. 2002-11-10 Simon Josefsson * basic.c (check_aes128_cbc_cts_cipher): New function. (check_one_cipher): Add flags parameter. (check_ciphers): Support flags parameter. (main): Check CTS. 2002-11-10 Werner Koch * basic.c (check_one_md): New. By Simon Josefsson. (check_digests): New tests for MD4. By Simon. 2002-08-26 Werner Koch * basic.c (check_ciphers): Check simple DES. 2002-05-16 Werner Koch * tsexp.c (back_and_forth): Very minimal test of the new functions. 2002-05-14 Werner Koch Changed license of all files to the LGPL. 2002-05-02 Werner Koch * basic.c: Add option --verbose. 2002-01-11 Werner Koch * tsexp.c (canon_len): Fixed tests. 2001-12-18 Werner Koch * tsexp.c: New. Copyright 2001, 2002, 2003, 2008 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without modifications, as long as this notice is preserved. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, to the extent permitted by law; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/tests/cavs_driver.pl b/tests/cavs_driver.pl index 2929410c..8556a81d 100755 --- a/tests/cavs_driver.pl +++ b/tests/cavs_driver.pl @@ -1,1687 +1,1711 @@ #!/usr/bin/env perl # -# Id: cavs_driver.pl 1236 2008-09-17 13:00:06Z smueller +# $Id: cavs_driver.pl 1243 2008-09-18 18:42:57Z smueller $ # # CAVS test driver (based on the OpenSSL driver) # Written by: Stephan Müller # 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 .rep # 3. send the resulting 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 # [CBC|CFB128|ECB|OFB]GFSbox[128|192|256] # [CBC|CFB128|ECB|OFB]MCT[128|192|256] # [CBC|CFB128|ECB|OFB]VarKey[128|192|256] # [CBC|CFB128|ECB|OFB]KeySbox[128|192|256] # [CBC|CFB128|ECB|OFB]MMT[128|192|256] # [CBC|CFB128|ECB|OFB]VarTxt[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 # SHA[1|224|256|384|512]ShortMsg # SHA[1|224|256|384|512]LongMsg # SHA[1|224|256|384|512]Monte # # HMAC (SHA - caveat: we only support hash output equal to the block size of # of the hash - we do not support truncation of the hash; to support # that, we first need to decipher the HMAC.req file - see hmac_kat() ) # HMAC # # TDES # T[CBC|CFB??|ECB|OFB]Monte[1|2|3] # T[CBC|CFB??|ECB|OFB]permop # T[CBC|CFB??|ECB|OFB]MMT[1|2|3] # T[CBC|CFB??|ECB|OFB]subtab # T[CBC|CFB??|ECB|OFB]varkey # T[CBC|CFB??|ECB|OFB]invperm # T[CBC|CFB??|ECB|OFB]vartext # # 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 (please note for 3DES: even when ede3 for three # independent ciphers is given with the cipher specification, we hand in # either one key for k1 = k2 = k3, two keys which are concatinated for # k1 = k3, k2 independent, or three keys which are concatinated for # k1, k2, k3 independent) # $2 iv in hex form # $3 cipher - the cipher string is defined as specified in the openssl # enc(1ssl) specification for the option "-ciphername" # (e.g. aes-128-cbc or des-ede3-cbc) # $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 with the following properties: # exponent is 65537 # PEM format # $1 key size in bit # $2 keyfile name # return: nothing, but file created my $gen_rsakey; # Creating a hash # $1: Plaintext in hex form # $2: hash type in the form documented in openssl's dgst(1ssl) - e.g. # sha1, sha224, sha256, sha384, sha512 # 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; # Generate an HMAC based on SHAx # $1: Key to be used for the HMAC in hex format # $2: length of the hash to be calculated in bits # $3: Message for which the HMAC shall be calculated in hex format # $4: hash type (1 - SHA1, 224 - SHA224, and so on) # return: calculated HMAC in hex format my $hmac; ################################################################ ##### 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_encdec($$$$$) { my $key=shift; my $iv=shift; my $cipher=shift; my $enc = (shift) ? "encrypt" : "decrypt"; my $data=shift; my $program="fipsdrv --no-fips --key $key --iv $iv --algo $cipher $enc"; return pipe_through_program($data,$program); } - sub libgcrypt_rsa_sign($$$) { my $data = shift; my $hashalgo = shift; my $keyfile = shift; die "ARCFOUR not available for RSA" if $opt{'R'}; return pipe_through_program($data, - "fipsdrv --verbose --algo $hashalgo --key $keyfile rsa-sign"); + "fipsdrv --verbose --pkcs1 --algo $hashalgo --key $keyfile rsa-sign"); } - sub libgcrypt_rsa_verify($$$$) { my $data = shift; - my $cipher = shift; + my $hashalgo = shift; my $keyfile = shift; my $sigfile = shift; - $data = hex2bin($data); die "ARCFOUR not available for RSA" if $opt{'R'}; $data = pipe_through_program($data, - "fipsdrv --verbose --algo $hashalgo --key $keyfile --signature $sigfile rsa-verify"); + "fipsdrv --verbose --pkcs1 --algo $hashalgo --key $keyfile --signature $sigfile rsa-verify"); # Parse through the output information return ($data =~ /GOOD signature/); } - sub libgcrypt_gen_rsakey($$) { my $keylen = shift; my $file = shift; die "ARCFOUR not available for RSA" if $opt{'R'}; my @args = ("fipsdrv --keysize $keylen rsa-gen > $file"); - system(@args) == 0 + system(@args) == 0 or die "system @args failed: $?"; die "system @args failed: file $file not created" if (! -f $file); } - sub libgcrypt_hash($$) { my $pt = shift; my $hashalgo = shift; - my $program = "fipsdrv --no-fips --algo $hashalgo digest"; + my $program = "fipsdrv --no-fips --algo $hashalgo digest"; die "ARCFOUR not available for hashes" if $opt{'R'}; - + return pipe_through_program($pt, $program); } - sub libgcrypt_state_cipher($$$$$) { my $cipher = shift; my $enc = (shift) ? "encrypt": "decrypt"; my $bufsize = shift; my $key = shift; my $iv = shift; my $program="fipsdrv --no-fips --binary --key ".bin2hex($key)." --iv ".bin2hex($iv)." --algo '$cipher' --chunk '$bufsize' $enc"; return $program; } - sub libgcrypt_state_rng($$$) { my $key = shift; my $dt = shift; my $v = shift; return "fipsdrv --binary --progress --loop --key $key --iv $v --dt $dt random"; } sub libgcrypt_hmac($$$$) { my $key = shift; my $maclen = shift; my $msg = shift; my $hashtype = shift; my $program = "fipsdrv --no-fips --key $key --algo $hashtype hmac-sha"; - return pipe_through_program($msg, $program); + return pipe_through_program($msg, $program); } ######### End of libgcrypt implementation ################ +################################################################ +###### Vendor1 interface functions +################################################################ + +sub vendor1_encdec($$$$$) { + my $key=shift; + my $iv=shift; + my $cipher=shift; + my $enc = (shift) ? "encrypt" : "decrypt"; + my $data=shift; + + $data=hex2bin($data); + my $program = "./aes $enc $key"; + $data=pipe_through_program($data,$program); + return bin2hex($data); +} + +sub vendor1_state_cipher($$$$$) { + my $cipher = shift; + my $encdec = shift; + my $bufsize = shift; + my $key = shift; + my $iv = shift; + + $key = bin2hex($key); + my $enc = $encdec ? "encrypt": "decrypt"; + my $out = "./aes $enc $key $bufsize"; + return $out; +} + ##### 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 = ""; $out .= "$keytype = $key1\n"; # this is the concardination of the keys for 3DES if (defined($key2)) { $out .= "KEY2 = $key2\n"; $key1 = $key1 . $key2; } if (defined($key3)) { $out .= "KEY3 = $key3\n"; $key1= $key1 . $key3; } - $out .= "IV = $iv\n"; + $out .= "IV = $iv\n" if (defined($iv) && $iv ne ""); 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; } # Known Answer Test for HMAC hash # $1: key length in bytes # $2: MAC length in bytes # $3: key for HMAC in hex form # $4: message to be hashed # return: string formatted as expected by CAVS sub hmac_kat($$$$) { my $klen = shift; my $tlen = shift; my $key = shift; my $msg = shift; # XXX this is a hack - we need to decipher the HMAC REQ files in a more # sane way # # This is a conversion table from the expected hash output size # to the assumed hash type - we only define here the block size of # the underlying hashes and do not allow any truncation my %hashtype = ( 20 => 1, 28 => 224, 32 => 256, 48 => 384, 64 => 512 ); die "Hash output size $tlen is not supported!" if(!defined($hashtype{$tlen})); my $out = ""; $out .= "Klen = $klen\n"; $out .= "Tlen = $tlen\n"; $out .= "Key = $key\n"; $out .= "Msg = $msg\n"; $out .= "Mac = " . &$hmac($key, $tlen, $msg, $hashtype{$tlen}) . "\n\n"; 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"; + $out .= "IV = ". bin2hex($iv) . "\n" + if (defined($iv) && $iv ne ""); 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"; + #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"; + #print STDERR "len=$len, bufsize=$bufsize\n"; die if $len ne $bufsize; - #print STDERR "calc_data=", bin2hex($calc_data), "\n"; + #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] [-I name] --R execution of ARCFOUR instead of OpenSSL --I NAME Use interface style NAME: - openssl OpenSSL (default) - libgcrypt Libgcrypt"; +-R execution of ARCFOUR instead of OpenSSL +-I NAME Use interface style NAME: + openssl OpenSSL (default) + libgcrypt Libgcrypt"; } # 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 $klen = ""; my $tlen = ""; my $mode = ""; open(IN, "<$infile"); while() { 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-|SigGen|SigVer|RC4VS|ANSI X9\.31|Hash sizes tested)/) { 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"; } elsif ($tmpline =~ /SHA-224/) { $cipher="sha224"; } elsif ($tmpline =~ /SHA-256/) { $cipher="sha256"; } elsif ($tmpline =~ /SHA-384/) { $cipher="sha384"; } elsif ($tmpline =~ /SHA-512/) { $cipher="sha512"; } #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 =~ /Hash sizes tested/) { $tt = 9; die "Interface function hmac for HMAC testing not defined for tested library" if (!defined($hmac)); } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /MCT/) { $tt = 8; die "Interface function state_rng for RNG MCT not defined for tested library" if (!defined($state_rng)); } elsif ($tmpline =~ /ANSI X9\.31/ && $tmpline =~ /VST/) { $tt = 7; die "Interface function state_rng for RNG KAT not defined for tested library" if (!defined($state_rng)); } elsif ($tmpline =~ /SigVer/ ) { $tt = 6; die "Interface function rsa_verify or gen_rsakey for RSA verification not defined for tested library" if (!defined($rsa_verify) || !defined($gen_rsakey)); } elsif ($tmpline =~ /SigGen/ ) { $tt = 5; die "Interface function rsa_sign or gen_rsakey for RSA sign not defined for tested library" if (!defined($rsa_sign) || !defined($gen_rsakey)); } elsif ($tmpline =~ /Monte|MCT|Carlo/ && $cipher eq "sha") { $tt = 4; die "Interface function hash for Hashing not defined for tested library" if (!defined($hash)); } elsif ($tmpline =~ /Monte|MCT|Carlo/) { $tt = 2; die "Interface function state_cipher for Stateful Cipher operation defined for tested library" if (!defined($state_cipher)); - } elsif ($cipher eq "sha" && $tt!=5 && $tt!=6) { + } elsif ($cipher =~ /^sha\d+/ && $tt!=5 && $tt!=6) { $tt = 3; die "Interface function hash for Hashing not defined for tested library" if (!defined($hash)); } else { $tt = 1; die "Interface function encdec for Encryption/Decryption not defined for tested library" if (!defined($encdec)); } } # 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 "KEY3 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; } elsif ($line =~ /^Klen\s*=\s*(.*)/) { # HMAC requests die "Klen seen twice - check input file" if ($klen ne ""); $klen=$1; } elsif ($line =~ /^Tlen\s*=\s*(.*)/) { # HMAC RNG requests die "Tlen seen twice - check input file" if ($tlen ne ""); $tlen=$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 "") { + if ($key1 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 "") { + if ($key1 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 == 9) { if ($klen ne "" && $tlen ne "" && $key1 ne "" && $pt ne "") { $out .= hmac_kat($klen, $tlen, $key1, $pt); $key1 = ""; $tlen = ""; $klen = ""; $pt = ""; } } 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("RI:", \%opt) or die "bad option"; ##### Set library - if ( ! defined $opt{'I'} || $opt{'I'} eq 'openssl' ) { - 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; - } elsif ( $opt{'I'} eq 'libgcrypt' ) { - print STDERR "Using libgcrypt interface functions\n"; - $encdec = \&libgcrypt_encdec; - $rsa_sign = \&libgcrypt_rsa_sign; - $rsa_verify = \&libgcrypt_rsa_verify; - $gen_rsakey = \&libgcrypt_gen_rsakey; - $hash = \&libgcrypt_hash; - $state_cipher = \&libgcrypt_state_cipher; - $state_rng = \&libgcrypt_state_rng; - $hmac = \&libgcrypt_hmac; + if ( ! defined $opt{'I'} || $opt{'I'} eq 'openssl' ) { + 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; + } elsif ( $opt{'I'} eq 'libgcrypt' ) { + print STDERR "Using libgcrypt interface functions\n"; + $encdec = \&libgcrypt_encdec; + $rsa_sign = \&libgcrypt_rsa_sign; + $rsa_verify = \&libgcrypt_rsa_verify; + $gen_rsakey = \&libgcrypt_gen_rsakey; + $hash = \&libgcrypt_hash; + $state_cipher = \&libgcrypt_state_cipher; + $state_rng = \&libgcrypt_state_rng; + $hmac = \&libgcrypt_hmac; } else { - die "Invalid interface option given"; + die "Invalid interface option given"; } 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/fipsdrv.c b/tests/fipsdrv.c index 68b48e9d..1d5efbf9 100644 --- a/tests/fipsdrv.c +++ b/tests/fipsdrv.c @@ -1,1584 +1,1687 @@ /* fipsdrv.c - A driver to help with FIPS CAVS tests. Copyright (C) 2008 Free Software Foundation, Inc. This file is part of Libgcrypt. Libgcrypt is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. Libgcrypt is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifndef HAVE_W32_SYSTEM # include #endif #include #include #include #define PGM "fipsdrv" #define my_isascii(c) (!((c) & 0x80)) #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) /* Verbose mode flag. */ static int verbose; /* Binary input flag. */ static int binary_input; /* Binary output flag. */ static int binary_output; /* Base64 output flag. */ static int base64_output; /* We need to know whetehr we are in loop_mode. */ static int loop_mode; /* ASN.1 classes. */ enum { UNIVERSAL = 0, APPLICATION = 1, ASNCONTEXT = 2, PRIVATE = 3 }; /* ASN.1 tags. */ enum { TAG_NONE = 0, TAG_BOOLEAN = 1, TAG_INTEGER = 2, TAG_BIT_STRING = 3, TAG_OCTET_STRING = 4, TAG_NULL = 5, TAG_OBJECT_ID = 6, TAG_OBJECT_DESCRIPTOR = 7, TAG_EXTERNAL = 8, TAG_REAL = 9, TAG_ENUMERATED = 10, TAG_EMBEDDED_PDV = 11, TAG_UTF8_STRING = 12, TAG_REALTIVE_OID = 13, TAG_SEQUENCE = 16, TAG_SET = 17, TAG_NUMERIC_STRING = 18, TAG_PRINTABLE_STRING = 19, TAG_TELETEX_STRING = 20, TAG_VIDEOTEX_STRING = 21, TAG_IA5_STRING = 22, TAG_UTC_TIME = 23, TAG_GENERALIZED_TIME = 24, TAG_GRAPHIC_STRING = 25, TAG_VISIBLE_STRING = 26, TAG_GENERAL_STRING = 27, TAG_UNIVERSAL_STRING = 28, TAG_CHARACTER_STRING = 29, TAG_BMP_STRING = 30 }; /* ASN.1 Parser object. */ struct tag_info { int class; /* Object class. */ unsigned long tag; /* The tag of the object. */ unsigned long length; /* Length of the values. */ int nhdr; /* Length of the header (TL). */ unsigned int ndef:1; /* The object has an indefinite length. */ unsigned int cons:1; /* This is a constructed object. */ }; /* Print a error message and exit the process with an error code. */ static void die (const char *format, ...) { va_list arg_ptr; va_start (arg_ptr, format); fputs (PGM ": ", stderr); vfprintf (stderr, format, arg_ptr); va_end (arg_ptr); exit (1); } static void showhex (const char *prefix, const void *buffer, size_t length) { const unsigned char *p = buffer; if (prefix) fprintf (stderr, PGM ": %s: ", prefix); while (length-- ) fprintf (stderr, "%02X", *p++); if (prefix) putc ('\n', stderr); } /* Convert STRING consisting of hex characters into its binary representation and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The function checks that the STRING will convert exactly to LENGTH bytes. The string is delimited by either end of string or a white space character. The function returns -1 on error or the length of the parsed string. */ static int hex2bin (const char *string, void *buffer, size_t length) { int i; const char *s = string; for (i=0; i < length; ) { if (!hexdigitp (s) || !hexdigitp (s+1)) return -1; /* Invalid hex digits. */ ((unsigned char*)buffer)[i++] = xtoi_2 (s); s += 2; } if (*s && (!my_isascii (*s) || !isspace (*s)) ) return -1; /* Not followed by Nul or white space. */ if (i != length) return -1; /* Not of expected length. */ if (*s) s++; /* Skip the delimiter. */ return s - string; } /* Convert STRING consisting of hex characters into its binary representation and return it as an allocated buffer. The valid length of the buffer is returned at R_LENGTH. The string is delimited by end of string. The function returns NULL on error. */ static void * hex2buffer (const char *string, size_t *r_length) { const char *s; unsigned char *buffer; size_t length; buffer = gcry_xmalloc (strlen(string)/2+1); length = 0; for (s=string; *s; s +=2 ) { if (!hexdigitp (s) || !hexdigitp (s+1)) return NULL; /* Invalid hex digits. */ ((unsigned char*)buffer)[length++] = xtoi_2 (s); } *r_length = length; return buffer; } /* Read a file from stream FP into a newly allocated buffer and return that buffer. The valid length of the buffer is stored at R_LENGTH. Returns NULL on failure. If decode is set, the file is assumed to be hex encoded and the decoded content is returned. */ static void * read_file (FILE *fp, int decode, size_t *r_length) { char *buffer; size_t buflen; size_t nread, bufsize = 0; *r_length = 0; #define NCHUNK 8192 #ifdef HAVE_DOSISH_SYSTEM setmode (fileno(fp), O_BINARY); #endif buffer = NULL; buflen = 0; do { bufsize += NCHUNK; if (!buffer) buffer = gcry_xmalloc (bufsize); else buffer = gcry_xrealloc (buffer, bufsize); nread = fread (buffer + buflen, 1, NCHUNK, fp); if (nread < NCHUNK && ferror (fp)) { gcry_free (buffer); return NULL; } buflen += nread; } while (nread == NCHUNK); #undef NCHUNK if (decode) { const char *s; char *p; for (s=buffer,p=buffer,nread=0; nread+1 < buflen; s += 2, nread +=2 ) { if (!hexdigitp (s) || !hexdigitp (s+1)) { gcry_free (buffer); return NULL; /* Invalid hex digits. */ } *(unsigned char*)p++ = xtoi_2 (s); } if (nread != buflen) { gcry_free (buffer); return NULL; /* Odd number of hex digits. */ } buflen = p - buffer; } *r_length = buflen; return buffer; } /* Do in-place decoding of base-64 data of LENGTH in BUFFER. Returns the new length of the buffer. Dies on error. */ static size_t base64_decode (char *buffer, size_t length) { static unsigned char const asctobin[128] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff }; int idx = 0; unsigned char val = 0; int c = 0; char *d, *s; int lfseen = 1; /* Find BEGIN line. */ for (s=buffer; length; length--, s++) { if (lfseen && *s == '-' && length > 11 && !memcmp (s, "-----BEGIN ", 11)) { for (; length && *s != '\n'; length--, s++) ; break; } lfseen = (*s == '\n'); } /* Decode until pad character or END line. */ for (d=buffer; length; length--, s++) { if (lfseen && *s == '-' && length > 9 && !memcmp (s, "-----END ", 9)) break; if ((lfseen = (*s == '\n')) || *s == ' ' || *s == '\r' || *s == '\t') continue; if (*s == '=') { /* Pad character: stop */ if (idx == 1) *d++ = val; break; } if ( (*s & 0x80) || (c = asctobin[*(unsigned char *)s]) == 0xff) die ("invalid base64 character %02X at pos %d detected\n", *(unsigned char*)s, (int)(s-buffer)); switch (idx) { case 0: val = c << 2; break; case 1: val |= (c>>4)&3; *d++ = val; val = (c<<4)&0xf0; break; case 2: val |= (c>>2)&15; *d++ = val; val = (c<<6)&0xc0; break; case 3: val |= c&0x3f; *d++ = val; break; } idx = (idx+1) % 4; } return d - buffer; } /* Parse the buffer at the address BUFFER which consists of the number of octets as stored at BUFLEN. Return the tag and the length part from the TLV triplet. Update BUFFER and BUFLEN on success. Checks that the encoded length does not exhaust the length of the provided buffer. */ static int parse_tag (unsigned char const **buffer, size_t *buflen, struct tag_info *ti) { int c; unsigned long tag; const unsigned char *buf = *buffer; size_t length = *buflen; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; /* Get the tag */ if (!length) return -1; /* Premature EOF. */ c = *buf++; length--; ti->nhdr++; ti->class = (c & 0xc0) >> 6; ti->cons = !!(c & 0x20); tag = (c & 0x1f); if (tag == 0x1f) { tag = 0; do { tag <<= 7; if (!length) return -1; /* Premature EOF. */ c = *buf++; length--; ti->nhdr++; tag |= (c & 0x7f); } while ( (c & 0x80) ); } ti->tag = tag; /* Get the length */ if (!length) return -1; /* Premature EOF. */ c = *buf++; length--; ti->nhdr++; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) ti->ndef = 1; else if (c == 0xff) return -1; /* Forbidden length value. */ else { unsigned long len = 0; int count = c & 0x7f; for (; count; count--) { len <<= 8; if (!length) return -1; /* Premature EOF. */ c = *buf++; length--; ti->nhdr++; len |= (c & 0xff); } ti->length = len; } if (ti->class == UNIVERSAL && !ti->tag) ti->length = 0; if (ti->length > length) return -1; /* Data larger than buffer. */ *buffer = buf; *buflen = length; return 0; } -/* Read the file FNAME assuming it is a PEM encoded private or public - key file and return an S-expression. With SHOW set, the key - parameters are printed. */ +/* Read the file FNAME assuming it is a PEM encoded private key file + and return an S-expression. With SHOW set, the key parameters are + printed. */ static gcry_sexp_t -read_key_file (const char *fname, int private, int show) +read_private_key_file (const char *fname, int show) { gcry_error_t err; FILE *fp; char *buffer; size_t buflen; const unsigned char *der; size_t derlen; struct tag_info ti; gcry_mpi_t keyparms[8]; - int n_keyparms = private? 8 : 2; + int n_keyparms = 8; int idx; gcry_sexp_t s_key; fp = fopen (fname, binary_input?"rb":"r"); if (!fp) die ("can't open `%s': %s\n", fname, strerror (errno)); buffer = read_file (fp, 0, &buflen); if (!buffer) die ("error reading `%s'\n", fname); fclose (fp); buflen = base64_decode (buffer, buflen); /* Parse the ASN.1 structure. */ der = (const unsigned char*)buffer; derlen = buflen; if ( parse_tag (&der, &derlen, &ti) || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) goto bad_asn1; if ( parse_tag (&der, &derlen, &ti) || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef) goto bad_asn1; if (ti.length != 1 || *der) goto bad_asn1; /* The value of the first integer is no 0. */ - der += ti.length; derlen += ti.length; + der += ti.length; derlen -= ti.length; for (idx=0; idx < n_keyparms; idx++) { if ( parse_tag (&der, &derlen, &ti) || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef) goto bad_asn1; if (show) { char prefix[2]; prefix[0] = idx < 8? "nedpq12u"[idx] : '?'; prefix[1] = 0; showhex (prefix, der, ti.length); } err = gcry_mpi_scan (keyparms+idx, GCRYMPI_FMT_USG, der, ti.length,NULL); if (err) die ("error scanning RSA parameter %d: %s\n", idx, gpg_strerror (err)); - der += ti.length; derlen += ti.length; + der += ti.length; derlen -= ti.length; } if (idx != n_keyparms) die ("not enough RSA key parameters\n"); gcry_free (buffer); - if (private) + /* Convert from OpenSSL parameter ordering to the OpenPGP order. */ + /* First check that p < q; if not swap p and q and recompute u. */ + if (gcry_mpi_cmp (keyparms[3], keyparms[4]) > 0) { - /* Convert from OpenSSL parameter ordering to the OpenPGP order. */ - /* First check that p < q; if not swap p and q and recompute u. */ - if (gcry_mpi_cmp (keyparms[3], keyparms[4]) > 0) - { - gcry_mpi_swap (keyparms[3], keyparms[4]); - gcry_mpi_invm (keyparms[7], keyparms[3], keyparms[4]); - } - - /* Build the S-expression. */ - err = gcry_sexp_build (&s_key, NULL, - "(private-key(rsa(n%m)(e%m)" - /**/ "(d%m)(p%m)(q%m)(u%m)))", - keyparms[0], keyparms[1], keyparms[2], - keyparms[3], keyparms[4], keyparms[7] ); + gcry_mpi_swap (keyparms[3], keyparms[4]); + gcry_mpi_invm (keyparms[7], keyparms[3], keyparms[4]); } - else + + /* Build the S-expression. */ + err = gcry_sexp_build (&s_key, NULL, + "(private-key(rsa(n%m)(e%m)" + /**/ "(d%m)(p%m)(q%m)(u%m)))", + keyparms[0], keyparms[1], keyparms[2], + keyparms[3], keyparms[4], keyparms[7] ); + if (err) + die ("error building S-expression: %s\n", gpg_strerror (err)); + + for (idx=0; idx < n_keyparms; idx++) + gcry_mpi_release (keyparms[idx]); + + return s_key; + + bad_asn1: + die ("invalid ASN.1 structure in `%s'\n", fname); + return NULL; /*NOTREACHED*/ +} + + +/* Read the file FNAME assuming it is a PEM encoded public key file + and return an S-expression. With SHOW set, the key parameters are + printed. */ +static gcry_sexp_t +read_public_key_file (const char *fname, int show) +{ + gcry_error_t err; + FILE *fp; + char *buffer; + size_t buflen; + const unsigned char *der; + size_t derlen; + struct tag_info ti; + gcry_mpi_t keyparms[2]; + int n_keyparms = 2; + int idx; + gcry_sexp_t s_key; + + fp = fopen (fname, binary_input?"rb":"r"); + if (!fp) + die ("can't open `%s': %s\n", fname, strerror (errno)); + buffer = read_file (fp, 0, &buflen); + if (!buffer) + die ("error reading `%s'\n", fname); + fclose (fp); + + buflen = base64_decode (buffer, buflen); + + /* Parse the ASN.1 structure. */ + der = (const unsigned char*)buffer; + derlen = buflen; + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) + goto bad_asn1; + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) + goto bad_asn1; + /* We skip the description of the key parameters and assume it is RSA. */ + der += ti.length; derlen -= ti.length; + + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_BIT_STRING || ti.class || ti.cons || ti.ndef) + goto bad_asn1; + if (ti.length < 1 || *der) + goto bad_asn1; /* The number of unused bits needs to be 0. */ + der += 1; derlen -= 1; + + /* Parse the BIT string. */ + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_SEQUENCE || ti.class || !ti.cons || ti.ndef) + goto bad_asn1; + + for (idx=0; idx < n_keyparms; idx++) { - err = gcry_sexp_build (&s_key, NULL, - "(public-key(rsa(n%m)(e%m)))", - keyparms[0], keyparms[1]); + if ( parse_tag (&der, &derlen, &ti) + || ti.tag != TAG_INTEGER || ti.class || ti.cons || ti.ndef) + goto bad_asn1; + if (show) + { + char prefix[2]; + prefix[0] = idx < 2? "ne"[idx] : '?'; + prefix[1] = 0; + showhex (prefix, der, ti.length); + } + err = gcry_mpi_scan (keyparms+idx, GCRYMPI_FMT_USG, der, ti.length,NULL); + if (err) + die ("error scanning RSA parameter %d: %s\n", idx, gpg_strerror (err)); + der += ti.length; derlen -= ti.length; } + if (idx != n_keyparms) + die ("not enough RSA key parameters\n"); + + gcry_free (buffer); + + /* Build the S-expression. */ + err = gcry_sexp_build (&s_key, NULL, + "(public-key(rsa(n%m)(e%m)))", + keyparms[0], keyparms[1] ); if (err) die ("error building S-expression: %s\n", gpg_strerror (err)); for (idx=0; idx < n_keyparms; idx++) gcry_mpi_release (keyparms[idx]); - + return s_key; - + bad_asn1: die ("invalid ASN.1 structure in `%s'\n", fname); return NULL; /*NOTREACHED*/ } + /* Read the file FNAME assuming it is a binary signature result and return an an S-expression suitable for gcry_pk_verify. */ static gcry_sexp_t read_sig_file (const char *fname) { gcry_error_t err; FILE *fp; char *buffer; size_t buflen; gcry_mpi_t tmpmpi; gcry_sexp_t s_sig; fp = fopen (fname, "rb"); if (!fp) die ("can't open `%s': %s\n", fname, strerror (errno)); buffer = read_file (fp, 0, &buflen); if (!buffer) die ("error reading `%s'\n", fname); fclose (fp); err = gcry_mpi_scan (&tmpmpi, GCRYMPI_FMT_USG, buffer, buflen, NULL); if (!err) err = gcry_sexp_build (&s_sig, NULL, "(sig-val(rsa(s %m)))", tmpmpi); if (err) die ("error building S-expression: %s\n", gpg_strerror (err)); gcry_mpi_release (tmpmpi); gcry_free (buffer); return s_sig; } static void print_buffer (const void *buffer, size_t length) { int writerr = 0; if (base64_output) { static const unsigned char bintoasc[64+1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"); const unsigned char *p; unsigned char inbuf[4]; char outbuf[4]; int idx, quads; idx = quads = 0; for (p = buffer; length; p++, length--) { inbuf[idx++] = *p; if (idx > 2) { outbuf[0] = bintoasc[(*inbuf>>2)&077]; outbuf[1] = bintoasc[(((*inbuf<<4)&060) |((inbuf[1] >> 4)&017))&077]; outbuf[2] = bintoasc[(((inbuf[1]<<2)&074) |((inbuf[2]>>6)&03))&077]; outbuf[3] = bintoasc[inbuf[2]&077]; if (fwrite (outbuf, 4, 1, stdout) != 1) writerr = 1; idx = 0; if (++quads >= (64/4)) { if (fwrite ("\n", 1, 1, stdout) != 1) writerr = 1; quads = 0; } } } if (idx) { outbuf[0] = bintoasc[(*inbuf>>2)&077]; if (idx == 1) { outbuf[1] = bintoasc[((*inbuf<<4)&060)&077]; outbuf[2] = outbuf[3] = '='; } else { outbuf[1] = bintoasc[(((*inbuf<<4)&060) |((inbuf[1]>>4)&017))&077]; outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077]; outbuf[3] = '='; } if (fwrite (outbuf, 4, 1, stdout) != 1) writerr = 1; quads++; } if (quads && fwrite ("\n", 1, 1, stdout) != 1) writerr = 1; } else if (binary_output) { if (fwrite (buffer, length, 1, stdout) != 1) writerr++; } else { const unsigned char *p = buffer; while (length-- && !ferror (stdout) ) printf ("%02X", *p++); if (ferror (stdout)) writerr++; } if (!writerr && fflush (stdout) == EOF) writerr++; if (writerr) { #ifndef HAVE_W32_SYSTEM if (loop_mode && errno == EPIPE) loop_mode = 0; else #endif die ("writing output failed: %s\n", strerror (errno)); } } static gcry_error_t init_external_rng_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) { return gcry_control (58, r_context, flags, key, keylen, seed, seedlen, dt, dtlen); } static gcry_error_t run_external_rng_test (void *context, void *buffer, size_t buflen) { return gcry_control (59, context, buffer, buflen); } static void deinit_external_rng_test (void *context) { gcry_control (60, context); } /* Given an OpenSSL cipher name NAME, return the Libgcrypt algirithm identified and store the libgcrypt mode at R_MODE. Returns 0 on error. */ static int map_openssl_cipher_name (const char *name, int *r_mode) { static struct { const char *name; int algo; int mode; } table[] = { { "bf-cbc", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC }, { "bf", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC }, { "bf-cfb", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB }, { "bf-ecb", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB }, { "bf-ofb", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB }, { "cast-cbc", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC }, { "cast", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC }, { "cast5-cbc", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CBC }, { "cast5-cfb", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_CFB }, { "cast5-ecb", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_ECB }, { "cast5-ofb", GCRY_CIPHER_CAST5, GCRY_CIPHER_MODE_OFB }, { "des-cbc", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC }, { "des", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC }, { "des-cfb", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CFB }, { "des-ofb", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_OFB }, { "des-ecb", GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB }, { "des-ede3-cbc", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC }, { "des-ede3 ", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB }, { "des3 ", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC }, { "des-ede3-cfb", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CFB }, { "des-ede3-ofb", GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_OFB }, { "rc4", GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM }, { "aes-128-cbc", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC }, { "aes-128", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC }, { "aes-128-cfb", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB }, { "aes-128-ecb", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB }, { "aes-128-ofb", GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OFB }, { "aes-192-cbc", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC }, { "aes-192", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC }, { "aes-192-cfb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB }, { "aes-192-ecb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB }, { "aes-192-ofb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB }, { "aes-256-cbc", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC }, { "aes-256", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC }, { "aes-256-cfb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB }, { "aes-256-ecb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB }, { "aes-256-ofb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB }, { NULL, 0 , 0 } }; int idx; for (idx=0; table[idx].name; idx++) if (!strcmp (name, table[idx].name)) { *r_mode = table[idx].mode; return table[idx].algo; } *r_mode = 0; return 0; } /* Run an encrypt or decryption operations. If DATA is NULL the function reads its input in chunks of size DATALEN from fp and processes it and writes it out until EOF. */ static void run_encrypt_decrypt (int encrypt_mode, int cipher_algo, int cipher_mode, const void *iv_buffer, size_t iv_buflen, const void *key_buffer, size_t key_buflen, const void *data, size_t datalen, FILE *fp) { gpg_error_t err; gcry_cipher_hd_t hd; void *outbuf; size_t outbuflen; void *inbuf; size_t inbuflen; err = gcry_cipher_open (&hd, cipher_algo, cipher_mode, 0); if (err) die ("gcry_cipher_open failed for algo %d, mode %d: %s\n", cipher_algo, cipher_mode, gpg_strerror (err)); err = gcry_cipher_setkey (hd, key_buffer, key_buflen); if (err) die ("gcry_cipher_setkey failed with keylen %u: %s\n", (unsigned int)key_buflen, gpg_strerror (err)); err = gcry_cipher_setiv (hd, iv_buffer, iv_buflen); if (err) die ("gcry_cipher_setiv failed with ivlen %u: %s\n", (unsigned int)iv_buflen, gpg_strerror (err)); inbuf = data? NULL : gcry_xmalloc (datalen); outbuflen = datalen; outbuf = gcry_xmalloc (outbuflen); do { if (inbuf) { int nread = fread (inbuf, 1, datalen, fp); if (nread < (int)datalen && ferror (fp)) die ("error reading input\n"); data = inbuf; inbuflen = nread; } else inbuflen = datalen; if (encrypt_mode) err = gcry_cipher_encrypt (hd, outbuf, outbuflen, data, inbuflen); else err = gcry_cipher_decrypt (hd, outbuf, outbuflen, data, inbuflen); if (err) die ("gcry_cipher_%scrypt failed: %s\n", encrypt_mode? "en":"de", gpg_strerror (err)); print_buffer (outbuf, outbuflen); } while (inbuf); gcry_cipher_close (hd); gcry_free (outbuf); gcry_free (inbuf); } /* Run a digest operation. */ static void run_digest (int digest_algo, const void *data, size_t datalen) { gpg_error_t err; gcry_md_hd_t hd; const unsigned char *digest; unsigned int digestlen; err = gcry_md_open (&hd, digest_algo, 0); if (err) die ("gcry_md_open failed for algo %d: %s\n", digest_algo, gpg_strerror (err)); gcry_md_write (hd, data, datalen); digest = gcry_md_read (hd, digest_algo); digestlen = gcry_md_get_algo_dlen (digest_algo); print_buffer (digest, digestlen); gcry_md_close (hd); } /* Run a HMAC operation. */ static void run_hmac (int digest_algo, const void *key, size_t keylen, const void *data, size_t datalen) { gpg_error_t err; gcry_md_hd_t hd; const unsigned char *digest; unsigned int digestlen; err = gcry_md_open (&hd, digest_algo, GCRY_MD_FLAG_HMAC); if (err) die ("gcry_md_open failed for HMAC algo %d: %s\n", digest_algo, gpg_strerror (err)); gcry_md_setkey (hd, key, keylen); if (err) die ("gcry_md_setkey failed for HMAC algo %d: %s\n", digest_algo, gpg_strerror (err)); gcry_md_write (hd, data, datalen); digest = gcry_md_read (hd, digest_algo); digestlen = gcry_md_get_algo_dlen (digest_algo); print_buffer (digest, digestlen); gcry_md_close (hd); } static size_t compute_tag_length (size_t n) { int needed = 0; if (n < 128) needed += 2; /* Tag and one length byte. */ else if (n < 256) needed += 3; /* Tag, number of length bytes, 1 length byte. */ else if (n < 65536) needed += 4; /* Tag, number of length bytes, 2 length bytes. */ else die ("DER object too long to encode\n"); return needed; } static unsigned char * store_tag_length (unsigned char *p, int tag, size_t n) { if (tag == TAG_SEQUENCE) tag |= 0x20; /* constructed */ *p++ = tag; if (n < 128) *p++ = n; else if (n < 256) { *p++ = 0x81; *p++ = n; } else if (n < 65536) { *p++ = 0x82; *p++ = n >> 8; *p++ = n; } return p; } /* Generate an RSA key of size KEYSIZE using the public exponent PUBEXP and print it to stdout in the OpenSSL format. The format is: SEQUENCE { INTEGER (0) -- Unknown constant. INTEGER -- n INTEGER -- e INTEGER -- d INTEGER -- p INTEGER -- q (with p < q) INTEGER -- dmp1 = d mod (p-1) INTEGER -- dmq1 = d mod (q-1) INTEGER -- u = p^{-1} mod q } */ static void run_rsa_gen (int keysize, int pubexp) { gpg_error_t err; gcry_sexp_t keyspec, key, l1; const char keyelems[] = "nedpq..u"; gcry_mpi_t keyparms[8]; size_t keyparmslen[8]; int idx; size_t derlen, needed, n; unsigned char *derbuf, *der; err = gcry_sexp_build (&keyspec, NULL, "(genkey (rsa (nbits %d)(rsa-use-e %d)))", keysize, pubexp); if (err) die ("gcry_sexp_build failed for RSA key generation: %s\n", gpg_strerror (err)); err = gcry_pk_genkey (&key, keyspec); if (err) die ("gcry_pk_genkey failed for RSA: %s\n", gpg_strerror (err)); gcry_sexp_release (keyspec); l1 = gcry_sexp_find_token (key, "private-key", 0); if (!l1) die ("private key not found in genkey result\n"); gcry_sexp_release (key); key = l1; l1 = gcry_sexp_find_token (key, "rsa", 0); if (!l1) die ("returned private key not formed as expected\n"); gcry_sexp_release (key); key = l1; /* Extract the parameters from the S-expression and store them in a well defined order in KEYPARMS. */ for (idx=0; idx < DIM(keyparms); idx++) { if (keyelems[idx] == '.') { keyparms[idx] = gcry_mpi_new (0); continue; } l1 = gcry_sexp_find_token (key, keyelems+idx, 1); if (!l1) die ("no %c parameter in returned private key\n", keyelems[idx]); keyparms[idx] = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); if (!keyparms[idx]) die ("no value for %c parameter in returned private key\n", keyelems[idx]); gcry_sexp_release (l1); } gcry_sexp_release (key); /* Check that p < q; if not swap p and q and recompute u. */ if (gcry_mpi_cmp (keyparms[3], keyparms[4]) > 0) { gcry_mpi_swap (keyparms[3], keyparms[4]); gcry_mpi_invm (keyparms[7], keyparms[3], keyparms[4]); } /* Compute the additional parameters. */ gcry_mpi_sub_ui (keyparms[5], keyparms[3], 1); gcry_mpi_mod (keyparms[5], keyparms[2], keyparms[5]); gcry_mpi_sub_ui (keyparms[6], keyparms[4], 1); gcry_mpi_mod (keyparms[6], keyparms[2], keyparms[6]); /* Compute the length of the DER encoding. */ needed = compute_tag_length (1) + 1; for (idx=0; idx < DIM(keyparms); idx++) { err = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, keyparms[idx]); if (err) die ("error formatting parameter: %s\n", gpg_strerror (err)); keyparmslen[idx] = n; needed += compute_tag_length (n) + n; } /* Store the key parameters. */ derlen = compute_tag_length (needed) + needed; der = derbuf = gcry_xmalloc (derlen); der = store_tag_length (der, TAG_SEQUENCE, needed); der = store_tag_length (der, TAG_INTEGER, 1); *der++ = 0; for (idx=0; idx < DIM(keyparms); idx++) { der = store_tag_length (der, TAG_INTEGER, keyparmslen[idx]); err = gcry_mpi_print (GCRYMPI_FMT_STD, der, keyparmslen[idx], NULL, keyparms[idx]); if (err) die ("error formatting parameter: %s\n", gpg_strerror (err)); der += keyparmslen[idx]; } /* Print the stuff. */ for (idx=0; idx < DIM(keyparms); idx++) gcry_mpi_release (keyparms[idx]); assert (der - derbuf == derlen); if (base64_output) puts ("-----BEGIN RSA PRIVATE KEY-----"); print_buffer (derbuf, derlen); if (base64_output) puts ("-----END RSA PRIVATE KEY-----"); gcry_free (derbuf); } /* Sign DATA of length DATALEN using the key taken from the PEM encoded KEYFILE and the hash algorithm HASHALGO. */ static void run_rsa_sign (const void *data, size_t datalen, int hashalgo, int pkcs1, const char *keyfile) { gpg_error_t err; gcry_sexp_t s_data, s_key, s_sig, s_tmp; gcry_mpi_t sig_mpi = NULL; unsigned char *outbuf; size_t outlen; /* showhex ("D", data, datalen); */ - if (pkcs1) - err = gcry_sexp_build (&s_data, NULL, - "(data (flags pkcs1)(hash %s %b))", - gcry_md_algo_name (hashalgo), (int)datalen, data); + { + unsigned char hash[50]; + unsigned int hashsize; + + hashsize = gcry_md_get_algo_dlen (hashalgo); + if (!hashsize || hashsize > sizeof hash) + die ("digest too long for buffer or unknown hash algorithm\n"); + gcry_md_hash_buffer (hashalgo, hash, data, datalen); + err = gcry_sexp_build (&s_data, NULL, + "(data (flags pkcs1)(hash %s %b))", + gcry_md_algo_name (hashalgo), + (int)hashsize, hash); + } else { gcry_mpi_t tmp; err = gcry_mpi_scan (&tmp, GCRYMPI_FMT_USG, data, datalen,NULL); if (!err) { err = gcry_sexp_build (&s_data, NULL, "(data (flags raw)(value %m))", tmp); gcry_mpi_release (tmp); } } if (err) die ("gcry_sexp_build failed for RSA data input: %s\n", gpg_strerror (err)); - s_key = read_key_file (keyfile, 1, 0); + s_key = read_private_key_file (keyfile, 0); err = gcry_pk_sign (&s_sig, s_data, s_key); if (err) { - gcry_sexp_release (read_key_file (keyfile, 1, 1)); + gcry_sexp_release (read_private_key_file (keyfile, 1)); die ("gcry_pk_signed failed (datalen=%d,keyfile=%s): %s\n", (int)datalen, keyfile, gpg_strerror (err)); } gcry_sexp_release (s_key); gcry_sexp_release (s_data); s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0); if (s_tmp) { gcry_sexp_release (s_sig); s_sig = s_tmp; s_tmp = gcry_sexp_find_token (s_sig, "rsa", 0); if (s_tmp) { gcry_sexp_release (s_sig); s_sig = s_tmp; s_tmp = gcry_sexp_find_token (s_sig, "s", 0); if (s_tmp) { gcry_sexp_release (s_sig); s_sig = s_tmp; sig_mpi = gcry_sexp_nth_mpi (s_sig, 1, GCRYMPI_FMT_USG); } } } gcry_sexp_release (s_sig); if (!sig_mpi) die ("no value in returned S-expression\n"); err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &outbuf, &outlen, sig_mpi); if (err) die ("gcry_mpi_aprint failed: %s\n", gpg_strerror (err)); gcry_mpi_release (sig_mpi); print_buffer (outbuf, outlen); gcry_free (outbuf); } /* Verify DATA of length DATALEN using the public key taken from the PEM encoded KEYFILE and the hash algorithm HASHALGO against the binary signature in SIGFILE. */ static void run_rsa_verify (const void *data, size_t datalen, int hashalgo, int pkcs1, const char *keyfile, const char *sigfile) { gpg_error_t err; gcry_sexp_t s_data, s_key, s_sig; if (pkcs1) - err = gcry_sexp_build (&s_data, NULL, - "(data (flags pkcs1)(hash %s %b))", - gcry_md_algo_name (hashalgo), (int)datalen, data); + { + unsigned char hash[64]; + unsigned int hashsize; + + hashsize = gcry_md_get_algo_dlen (hashalgo); + if (!hashsize || hashsize > sizeof hash) + die ("digest too long for buffer or unknown hash algorithm\n"); + gcry_md_hash_buffer (hashalgo, hash, data, datalen); + err = gcry_sexp_build (&s_data, NULL, + "(data (flags pkcs1)(hash %s %b))", + gcry_md_algo_name (hashalgo), + (int)hashsize, hash); + } else { gcry_mpi_t tmp; err = gcry_mpi_scan (&tmp, GCRYMPI_FMT_USG, data, datalen,NULL); if (!err) { err = gcry_sexp_build (&s_data, NULL, "(data (flags raw)(value %m))", tmp); gcry_mpi_release (tmp); } } if (err) die ("gcry_sexp_build failed for RSA data input: %s\n", gpg_strerror (err)); - s_key = read_key_file (keyfile, 0, 0); + s_key = read_public_key_file (keyfile, 0); s_sig = read_sig_file (sigfile); err = gcry_pk_verify (s_sig, s_data, s_key); if (!err) - puts ("GOOD signature\n"); + puts ("GOOD signature"); else if (gpg_err_code (err) == GPG_ERR_BAD_SIGNATURE) - puts ("BAD signature\n"); + puts ("BAD signature"); else printf ("ERROR (%s)\n", gpg_strerror (err)); gcry_sexp_release (s_sig); gcry_sexp_release (s_key); gcry_sexp_release (s_data); } static void usage (int show_help) { if (!show_help) { fputs ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n", stderr); exit (2); } fputs ("Usage: " PGM " [OPTIONS] MODE [FILE]\n" "Run a crypto operation using hex encoded input and output.\n" "MODE:\n" " encrypt, decrypt, digest, random, hmac-sha, rsa-{gen,sign,verify}\n" "OPTIONS:\n" " --verbose print additional information\n" " --binary input and output is in binary form\n" " --no-fips do not force FIPS mode\n" " --key KEY use the hex encoded KEY\n" " --iv IV use the hex encoded IV\n" " --dt DT use the hex encoded DT for the RNG\n" " --algo NAME use algorithm NAME\n" " --keysize N use a keysize of N bits\n" " --signature NAME take signature from file NAME\n" " --chunk N read in chunks of N bytes (implies --binary)\n" " --pkcs1 use PKCS#1 encoding\n" " --loop enable random loop mode\n" " --progress print pogress indicators\n" " --help print this text\n" "With no FILE, or when FILE is -, read standard input.\n" "Report bugs to " PACKAGE_BUGREPORT ".\n" , stdout); exit (0); } int main (int argc, char **argv) { int last_argc = -1; gpg_error_t err; int no_fips = 0; int progress = 0; int use_pkcs1 = 0; const char *mode_string; const char *key_string = NULL; const char *iv_string = NULL; const char *dt_string = NULL; const char *algo_string = NULL; const char *keysize_string = NULL; const char *signature_string = NULL; FILE *input; void *data; size_t datalen; size_t chunksize = 0; if (argc) { argc--; argv++; } while (argc && last_argc != argc ) { last_argc = argc; if (!strcmp (*argv, "--")) { argc--; argv++; break; } else if (!strcmp (*argv, "--help")) { usage (1); } else if (!strcmp (*argv, "--version")) { fputs (PGM " (Libgcrypt) " PACKAGE_VERSION "\n", stdout); exit (0); } else if (!strcmp (*argv, "--verbose")) { verbose++; argc--; argv++; } else if (!strcmp (*argv, "--binary")) { binary_input = binary_output = 1; argc--; argv++; } else if (!strcmp (*argv, "--no-fips")) { no_fips++; argc--; argv++; } else if (!strcmp (*argv, "--loop")) { loop_mode = 1; argc--; argv++; } else if (!strcmp (*argv, "--progress")) { progress = 1; argc--; argv++; } else if (!strcmp (*argv, "--key")) { argc--; argv++; if (!argc) usage (0); key_string = *argv; argc--; argv++; } else if (!strcmp (*argv, "--iv")) { argc--; argv++; if (!argc) usage (0); iv_string = *argv; argc--; argv++; } else if (!strcmp (*argv, "--dt")) { argc--; argv++; if (!argc) usage (0); dt_string = *argv; argc--; argv++; } else if (!strcmp (*argv, "--algo")) { argc--; argv++; if (!argc) usage (0); algo_string = *argv; argc--; argv++; } else if (!strcmp (*argv, "--keysize")) { argc--; argv++; if (!argc) usage (0); keysize_string = *argv; argc--; argv++; } else if (!strcmp (*argv, "--signature")) { argc--; argv++; if (!argc) usage (0); signature_string = *argv; argc--; argv++; } else if (!strcmp (*argv, "--chunk")) { argc--; argv++; if (!argc) usage (0); chunksize = atoi (*argv); binary_input = binary_output = 1; argc--; argv++; } else if (!strcmp (*argv, "--pkcs1")) { use_pkcs1 = 1; argc--; argv++; } } if (!argc || argc > 2) usage (0); mode_string = *argv; if (argc == 2 && strcmp (argv[1], "-")) { input = fopen (argv[1], binary_input? "rb":"r"); if (!input) die ("can't open `%s': %s\n", argv[1], strerror (errno)); } else input = stdin; #ifndef HAVE_W32_SYSTEM if (loop_mode) signal (SIGPIPE, SIG_IGN); #endif if (verbose) fprintf (stderr, PGM ": started (mode=%s)\n", mode_string); gcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose); if (!no_fips) gcry_control (GCRYCTL_FORCE_FIPS_MODE, 0); if (!gcry_check_version ("1.4.3")) die ("Libgcrypt is not sufficient enough\n"); if (verbose) fprintf (stderr, PGM ": using Libgcrypt %s\n", gcry_check_version (NULL)); if (no_fips) gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); /* Most operations need some input data. */ if (!chunksize && strcmp (mode_string, "random") && strcmp (mode_string, "rsa-gen") ) { data = read_file (input, !binary_input, &datalen); if (!data) die ("error reading%s input\n", binary_input?"":" and decoding"); if (verbose) fprintf (stderr, PGM ": %u bytes of input data\n", (unsigned int)datalen); } else { data = NULL; datalen = 0; } if (!strcmp (mode_string, "encrypt") || !strcmp (mode_string, "decrypt")) { int cipher_algo, cipher_mode; void *iv_buffer, *key_buffer; size_t iv_buflen, key_buflen; if (!algo_string) die ("option --algo is required in this mode\n"); cipher_algo = map_openssl_cipher_name (algo_string, &cipher_mode); if (!cipher_algo) die ("cipher algorithm `%s' is not supported\n", algo_string); if (!iv_string) die ("option --iv is required in this mode\n"); iv_buffer = hex2buffer (iv_string, &iv_buflen); if (!iv_buffer) die ("invalid value for IV\n"); if (!key_string) die ("option --key is required in this mode\n"); key_buffer = hex2buffer (key_string, &key_buflen); if (!key_buffer) die ("invalid value for KEY\n"); run_encrypt_decrypt ((*mode_string == 'e'), cipher_algo, cipher_mode, iv_buffer, iv_buflen, key_buffer, key_buflen, data, data? datalen:chunksize, input); gcry_free (key_buffer); gcry_free (iv_buffer); } else if (!strcmp (mode_string, "digest")) { int algo; if (!algo_string) die ("option --algo is required in this mode\n"); algo = gcry_md_map_name (algo_string); if (!algo) die ("digest algorithm `%s' is not supported\n", algo_string); run_digest (algo, data, datalen); } else if (!strcmp (mode_string, "random")) { void *context; unsigned char key[16]; unsigned char seed[16]; unsigned char dt[16]; unsigned char buffer[16]; size_t count = 0; if (hex2bin (key_string, key, 16) < 0 ) die ("value for --key are not 32 hex digits\n"); if (hex2bin (iv_string, seed, 16) < 0 ) die ("value for --iv are not 32 hex digits\n"); if (hex2bin (dt_string, dt, 16) < 0 ) die ("value for --dt are not 32 hex digits\n"); /* The flag value 1 disables the dup check, so that the RNG returns all generated data. */ err = init_external_rng_test (&context, 1, key, 16, seed, 16, dt, 16); if (err) die ("init external RNG test failed: %s\n", gpg_strerror (err)); do { err = run_external_rng_test (context, buffer, sizeof buffer); if (err) die ("running external RNG test failed: %s\n", gpg_strerror (err)); print_buffer (buffer, sizeof buffer); if (progress) { if (!(++count % 1000)) fprintf (stderr, PGM ": %lu random bytes so far\n", (unsigned long int)count * sizeof buffer); } } while (loop_mode); if (progress) fprintf (stderr, PGM ": %lu random bytes\n", (unsigned long int)count * sizeof buffer); deinit_external_rng_test (context); } else if (!strcmp (mode_string, "hmac-sha")) { int algo; void *key_buffer; size_t key_buflen; if (!data) die ("no data available (do not use --chunk)\n"); if (!algo_string) die ("option --algo is required in this mode\n"); switch (atoi (algo_string)) { case 1: algo = GCRY_MD_SHA1; break; case 224: algo = GCRY_MD_SHA224; break; case 256: algo = GCRY_MD_SHA256; break; case 384: algo = GCRY_MD_SHA384; break; case 512: algo = GCRY_MD_SHA512; break; default: algo = 0; break; } if (!algo) die ("no digest algorithm found for hmac type `%s'\n", algo_string); if (!key_string) die ("option --key is required in this mode\n"); key_buffer = hex2buffer (key_string, &key_buflen); if (!key_buffer) die ("invalid value for KEY\n"); run_hmac (algo, key_buffer, key_buflen, data, datalen); gcry_free (key_buffer); } else if (!strcmp (mode_string, "rsa-gen")) { int keysize; if (!binary_output) base64_output = 1; keysize = keysize_string? atoi (keysize_string) : 0; if (keysize < 128 || keysize > 16384) die ("invalid keysize specified; needs to be 128 .. 16384\n"); run_rsa_gen (keysize, 65537); } else if (!strcmp (mode_string, "rsa-sign")) { int algo; if (!key_string) die ("option --key is required in this mode\n"); if (access (key_string, R_OK)) die ("option --key needs to specify an existing keyfile\n"); if (!algo_string) die ("option --algo is required in this mode\n"); algo = gcry_md_map_name (algo_string); if (!algo) die ("digest algorithm `%s' is not supported\n", algo_string); if (!data) die ("no data available (do not use --chunk)\n"); run_rsa_sign (data, datalen, algo, use_pkcs1, key_string); } else if (!strcmp (mode_string, "rsa-verify")) { int algo; if (!key_string) die ("option --key is required in this mode\n"); if (access (key_string, R_OK)) die ("option --key needs to specify an existing keyfile\n"); if (!algo_string) die ("option --algo is required in this mode\n"); algo = gcry_md_map_name (algo_string); if (!algo) die ("digest algorithm `%s' is not supported\n", algo_string); if (!data) die ("no data available (do not use --chunk)\n"); if (!signature_string) die ("option --signature is required in this mode\n"); if (access (signature_string, R_OK)) die ("option --signature needs to specify an existing file\n"); run_rsa_verify (data, datalen, algo, use_pkcs1, key_string, signature_string); } else usage (0); gcry_free (data); /* Because Libgcrypt does not enforce FIPS mode in all cases we let the process die if Libgcrypt is not anymore in FIPS mode after the actual operation. */ if (!no_fips && !gcry_fips_mode_active ()) die ("FIPS mode is not anymore active\n"); if (verbose) fputs (PGM ": ready\n", stderr); return 0; }