home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / crypl200.zip / TESTAPP.C < prev    next >
Text File  |  1996-10-12  |  27KB  |  797 lines

  1. #include <ctype.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include "capi.h"
  6.  
  7. /* There are a few OS's broken enough not to define the standard exit codes
  8.    (SunOS springs to mind) so we define some sort of equivalent here just
  9.    in case */
  10.  
  11. #ifndef EXIT_SUCCESS
  12.   #define EXIT_SUCCESS    0
  13.   #define EXIT_FAILURE    !EXIT_SUCCESS
  14. #endif /* EXIT_SUCCESS */
  15. #ifndef SEEK_SET
  16.   #define SEEK_SET        0
  17.   #define SEEK_CUR        1
  18.   #define SEEK_END        2
  19. #endif /* SEEK_SET */
  20.  
  21. /* The names of the test external public and private key files */
  22.  
  23. #define PGP_PUBKEY_FILE        "keymgmt/testpgp.pub"
  24. #define PGP_PRIVKEY_FILE    "keymgmt/testpgp.prv"
  25. #define X509_PUBKEY_FILE    "keymgmt/testx509.pub"
  26. #define X509_PRIVKEY_FILE    "keymgmt/serverke.der"
  27.  
  28. /* The password for the test key */
  29.  
  30. #define KEY_PASSWORD        "test"
  31.  
  32. /* Various useful types */
  33.  
  34. typedef unsigned char    BYTE;
  35.  
  36. /* The encryption routines need to be careful about cleaning up allocated
  37.    memory which contains sensitive information.  Ideally we would throw an
  38.    exception which takes care of this, but we can't really do this without
  39.    assuming a C++ compiler.  As a tradeoff the following macro, which just
  40.    evaluates to a goto, is used to indicate that we'd do something nicer
  41.    here if we could */
  42.  
  43. #define THROW( x )    goto x
  44.  
  45. /****************************************************************************
  46. *                                                                            *
  47. *                Sample Application - Encrypt and Sign a File                *
  48. *                                                                            *
  49. ****************************************************************************/
  50.  
  51. /* Take an input file, sign it if necessary, encrypt it if necessary, and
  52.    write the result to an output file.  This function can produce any of:
  53.  
  54.     - A raw data object
  55.     - A signed data object
  56.     - A conventionally encrypted data object
  57.     - A public-key encrypted data object
  58.     - A conventionally encrypted, signed data object
  59.     - A public-key encrypted, signed data object
  60.  
  61.    depending on whether the signContext and cryptContext parameters are
  62.    empty or contain valid signature/encryption contexts.
  63.  
  64.    This function makes extensive use of the cryptlib object management
  65.    functions to ease the processing of the various objects */
  66.  
  67. int wrapFile( FILE *inFile, FILE *outFile, CRYPT_CONTEXT signContext,
  68.               CRYPT_CONTEXT cryptContext, CRYPT_ALGO cryptAlgo )
  69.     {
  70.     CRYPT_CONTEXT sessionKeyContext = 0, hashContext = 0;
  71.     CRYPT_IOCTLINFO_COOKIE cryptIoctlInfo;
  72.     BYTE *objectBuffer = NULL, *encryptedObject, *signedObject, *dataObject;
  73.     BYTE *signature;
  74.     int exportedKeySize = 0, encryptedObjectSize = 0, signedObjectSize = 0;
  75.     int dataObjectSize, objectSize, signatureSize = 0, status = CRYPT_ERROR;
  76.     long dataLength;
  77.  
  78.     /* Find out how big the input file is */
  79.     fseek( inFile, 0, SEEK_END );
  80.     dataLength = ftell( inFile );
  81.     fseek( inFile, 0, SEEK_SET );
  82.  
  83.     /* Create an encryption context for the session key and a session key if
  84.        we want to encrypt and a hash context for the signature if we want to
  85.        sign the data */
  86.     if( cryptContext )
  87.         {
  88.         status = cryptCreateContext( &sessionKeyContext, cryptAlgo, CRYPT_MODE_CFB );
  89.         if( cryptStatusError( status ) )
  90.             return( status );
  91.         status = cryptGenerateContext( sessionKeyContext );
  92.         if( cryptStatusError( status ) )
  93.             THROW( exception );
  94.         }
  95.     if( signContext )
  96.         {
  97.         status = cryptCreateContext( &hashContext, CRYPT_ALGO_SHA, CRYPT_MODE_NONE );
  98.         if( cryptStatusError( status ) )
  99.             THROW( exception );
  100.  
  101.         /* Since the Signature object directly follows the SignedData object
  102.            we don't bother exporting the signature cookie.  If we were using
  103.            a detached signature we would use the default behaviour of
  104.            exporting the cookie */
  105.         cryptIoctlInfo.exportCookie = 0;
  106.         cryptIoctl( CRYPT_IOCTL_SIGCOOKIE, &cryptIoctlInfo, hashContext );
  107.         }
  108.  
  109.     /* Find out how big the various objects will be.  Note the way we
  110.        determine the size of the nested objects: First the inner RawData
  111.        object, then the SignedData object surrounding it, then the
  112.        EncryptedData object surrounding that */
  113.     cryptExportObject( NULL, &dataObjectSize, CRYPT_OBJECT_RAW_DATA,
  114.                        dataLength );
  115.     if( signContext )
  116.         {
  117.         /* SignedData followed by the Signature */
  118.         cryptExportObjectEx( NULL, &signedObjectSize, CRYPT_OBJECT_SIGNED_DATA,
  119.                              dataObjectSize + dataLength, hashContext );
  120.         cryptCreateSignature( NULL, &signatureSize, signContext, hashContext );
  121.         }
  122.     if( cryptContext )
  123.         {
  124.         /* EncryptedKey/PKCEncryptedKey followed by the EncryptedData */
  125.         cryptExportKey( NULL, &exportedKeySize, cryptContext, sessionKeyContext );
  126.         cryptExportObjectEx( NULL, &encryptedObjectSize,
  127.                              CRYPT_OBJECT_ENCRYPTED_DATA, signedObjectSize +
  128.                              dataObjectSize + dataLength + signatureSize,
  129.                              sessionKeyContext );
  130.         }
  131.  
  132.     /* Allocate a buffer for the various objects and find the locations of
  133.        each object in the buffer */
  134.     if( ( objectBuffer = malloc( exportedKeySize + encryptedObjectSize +
  135.                                  signedObjectSize + dataObjectSize +
  136.                                  signatureSize ) ) == NULL )
  137.         THROW( exception );
  138.     encryptedObject = objectBuffer + exportedKeySize;
  139.     signedObject = encryptedObject + encryptedObjectSize;
  140.     dataObject = signedObject + signedObjectSize;
  141.     signature = dataObject + dataObjectSize;
  142.  
  143.     /* Export the session key */
  144.     if( cryptContext )
  145.         {
  146.         status = cryptExportKey( objectBuffer, &exportedKeySize, cryptContext,
  147.                                  sessionKeyContext );
  148.         if( cryptStatusError( status ) )
  149.             THROW( exception );
  150.         }
  151.  
  152.     /* Assemble the EncryptedData, SignedData, and RawData objects */
  153.     if( cryptContext )
  154.         cryptExportObjectEx( encryptedObject, &encryptedObjectSize,
  155.                              CRYPT_OBJECT_ENCRYPTED_DATA, signedObjectSize +
  156.                              dataObjectSize + dataLength + signatureSize,
  157.                              sessionKeyContext );
  158.     if( signContext )
  159.         cryptExportObjectEx( signedObject, &signedObjectSize,
  160.                              CRYPT_OBJECT_SIGNED_DATA, dataObjectSize +
  161.                              dataLength, hashContext );
  162.     cryptExportObject( dataObject, &dataObjectSize, CRYPT_OBJECT_RAW_DATA,
  163.                        dataLength );
  164.  
  165.     /* Hash the start of the RawData object and encrypt the start of the
  166.        SignedData object */
  167.     if( signContext )
  168.         cryptEncrypt( hashContext, dataObject, dataObjectSize );
  169.     if( cryptContext )
  170.         cryptEncrypt( sessionKeyContext, signedObject, signedObjectSize +
  171.                       dataObjectSize );
  172.  
  173.     /* Write the objects to the output file */
  174.     objectSize = exportedKeySize + encryptedObjectSize + signedObjectSize +
  175.                  dataObjectSize;
  176.     if( fwrite( objectBuffer, 1, objectSize, outFile ) != ( size_t ) objectSize )
  177.         THROW( exception );
  178.  
  179.     /* Now process the input file which contains the payload portion of the
  180.        RawData object.  We just read the file in chunks, hash it, encrypt it,
  181.        and write the result to the output file */
  182.     while( !feof( inFile ) )
  183.         {
  184.         BYTE buffer[ BUFSIZ * 4 ];
  185.         int bufferLength;
  186.  
  187.         if( ( bufferLength = fread( buffer, 1, BUFSIZ * 4, inFile ) ) == 0 )
  188.             break;
  189.         if( signContext )
  190.             cryptEncrypt( hashContext, buffer, bufferLength );
  191.         if( cryptContext )
  192.             cryptEncrypt( sessionKeyContext, buffer, bufferLength );
  193.         if( fwrite( buffer, 1, bufferLength, outFile ) != ( size_t ) bufferLength )
  194.             THROW( exception );
  195.         }
  196.  
  197.     /* We've passed the last general exception point, set the return value to
  198.        OK */
  199.     status = CRYPT_OK;
  200.  
  201.     /* Complete the hash and create the Signature object */
  202.     if( signContext )
  203.         {
  204.         cryptEncrypt( hashContext, signature, 0 );
  205.         status = cryptCreateSignature( signature, &signatureSize, signContext,
  206.                                        hashContext );
  207.         cryptDestroyContext( hashContext );
  208.         hashContext = 0;
  209.         if( cryptStatusError( status ) )
  210.             THROW( exception );
  211.  
  212.         /* Encrypt the Signature object and write it to the output file */
  213.         if( cryptContext )
  214.             status = cryptEncrypt( sessionKeyContext, signature,
  215.                                    signatureSize );
  216.         if( cryptStatusError( status ) )
  217.             THROW( exception );
  218.         if( fwrite( signature, 1, signatureSize, outFile ) != ( size_t ) signatureSize )
  219.             status = CRYPT_ERROR;
  220.         }
  221.     cryptDestroyContext( sessionKeyContext );
  222.  
  223.     /* Clean up */
  224.     free( objectBuffer );
  225.     return( status );
  226.  
  227.     /* Exception handlers */
  228. exception:
  229.     if( hashContext )
  230.         cryptDestroyContext( hashContext );
  231.     if( sessionKeyContext )
  232.         cryptDestroyContext( sessionKeyContext );
  233.     if( objectBuffer != NULL )
  234.         free( objectBuffer );
  235.     return( status );
  236.     }
  237.  
  238. /* Take an input file, unwrap the data objects, decrypt the data we need,
  239.    check the signature, and write the result to an output file.
  240.  
  241.    This code can't unwrap a standalone raw data object (with no encryption or
  242.    a signature), but this isn't worth fixing since it's been superseded by
  243.    the cryptEnvelope() functions (which, however, aren't present in the 2.00
  244.    release) */
  245.  
  246. int unwrapFile( FILE *inFile, FILE *outFile, const char *password )
  247.     {
  248.     CRYPT_CONTEXT signContext = 0, decryptContext = 0;
  249.     CRYPT_CONTEXT sessionKeyContext = 0, hashContext = 0;
  250.     CRYPT_OBJECT_INFO cryptObjectInfo;
  251.     BYTE *objectBuffer, *ioBuffer, *dataPtr, *signature;
  252.     int payloadStart, dataInBuffer, count, status = CRYPT_ERROR;
  253.     long payloadSize, dataSize, signatureSize = 0;
  254.  
  255.     /* Allocate room for the objects and read them into memory */
  256.     if( ( objectBuffer = malloc( 4096 ) ) == NULL )
  257.         return( CRYPT_NOMEM );
  258.     if( ( ioBuffer = malloc( 4096 ) ) == NULL )
  259.         {
  260.         free( objectBuffer );
  261.         return( CRYPT_NOMEM );
  262.         }
  263.     if( ( dataInBuffer = fread( objectBuffer, 1, 4096, inFile ) ) < 50 )
  264.         THROW( exception );
  265.     dataPtr = objectBuffer;
  266.  
  267.     /* Find out what we've got */
  268.     status = cryptQueryObject( objectBuffer, &cryptObjectInfo );
  269.     if( cryptStatusError( status ) )
  270.         THROW( exception );
  271.  
  272.     /* Process an EncryptedData object preceded by an EncryptedKey or
  273.        PKCEncryptedKey object */
  274.     if( cryptObjectInfo.type == CRYPT_OBJECT_PKCENCRYPTED_KEY || \
  275.         cryptObjectInfo.type == CRYPT_OBJECT_ENCRYPTED_KEY )
  276.         {
  277.         CRYPT_KEYSET cryptKeyset;
  278.  
  279.         /* Open the external key collection and try to read the required key */
  280.         status = cryptExtKeysetOpen( &cryptKeyset, PGP_PRIVKEY_FILE,
  281.                                      CRYPT_KEYSET_PGP );
  282.         if( cryptStatusError( status ) )
  283.             THROW( exception );
  284.         status = cryptGetKeyFromObjectEx( cryptKeyset, &decryptContext,
  285.                                           dataPtr, password );
  286.         if( cryptStatusError( status ) )
  287.             THROW( exception );
  288.         cryptKeysetClose( cryptKeyset );
  289.  
  290.         /* Query the encrypted key object to find its size */
  291.         status = cryptImportObject( dataPtr, &payloadStart, &payloadSize );
  292.         if( cryptStatusError( status ) )
  293.             THROW( exception );
  294.  
  295.         /* Recreate the session key by importing the encrypted key */
  296.         status = cryptImportKey( dataPtr, decryptContext,
  297.                                  &sessionKeyContext );
  298.         cryptDestroyContext( decryptContext );
  299.         decryptContext = 0;
  300.         if( cryptStatusError( status ) )
  301.             THROW( exception );
  302.  
  303.         /* Set up the session key to decrypt the encrypted data (there's no
  304.            payload for an encrypted session key so we don't need to include
  305.            the payload size in the calculation) */
  306.         dataPtr += payloadStart;
  307.         status = cryptImportObjectEx( dataPtr, &payloadStart, &payloadSize,
  308.                                       &sessionKeyContext );
  309.         if( cryptStatusError( status ) )
  310.             THROW( exception );
  311.  
  312.         /* Decrypt as much of the SignedData or RawData object as we have in
  313.            the buffer and remember how much we still have to process */
  314.         dataPtr += payloadStart;
  315.         dataInBuffer -= ( int ) ( dataPtr - objectBuffer );
  316.         status = cryptDecrypt( sessionKeyContext, dataPtr, dataInBuffer );
  317.         if( cryptStatusError( status ) )
  318.             THROW( exception );
  319.  
  320.         /* Adjust the total data size by the amount we've just decrypted */
  321.         dataSize = payloadSize - dataInBuffer;
  322.  
  323.         /* Find out what's next */
  324.         status = cryptQueryObject( dataPtr, &cryptObjectInfo );
  325.         if( cryptStatusError( status ) )
  326.             THROW( exception );
  327.         }
  328.  
  329.     /* Process a SignedData object */
  330.     if( cryptObjectInfo.type == CRYPT_OBJECT_SIGNED_DATA )
  331.         {
  332.         /* Import the signed data object */
  333.         status = cryptImportObjectEx( dataPtr, &payloadStart, &payloadSize,
  334.                                       &hashContext );
  335.         if( cryptStatusError( status ) )
  336.             THROW( exception );
  337.  
  338.         /* Hash as much of the RawData object as we have in the buffer and
  339.            remember how much we still have to process */
  340.         dataInBuffer -= payloadStart;
  341.         dataPtr += payloadStart;
  342.         dataSize = payloadSize - dataInBuffer;
  343.         if( dataSize < 0 )
  344.             {
  345.             /* If we've got some of the Signature object in the buffer,
  346.                don't hash the signature itself and remember how much of the
  347.                signature is already present */
  348.             signatureSize = -dataSize;
  349.             signature = dataPtr + ( int ) payloadSize;
  350.             dataSize = 0;
  351.             cryptDecrypt( hashContext, dataPtr, dataInBuffer -
  352.                           ( int ) signatureSize );
  353.             }
  354.         else
  355.             {
  356.             /* Everything in the buffer is SignedData, hash the whole thing */
  357.             signatureSize = 0;
  358.             cryptDecrypt( hashContext, dataPtr, dataInBuffer );
  359.             }
  360.  
  361.         /* Find out what's next */
  362.         status = cryptQueryObject( dataPtr, &cryptObjectInfo );
  363.         if( cryptStatusError( status ) )
  364.             THROW( exception );
  365.         }
  366.  
  367.     /* Process a RawData object */
  368.     if( cryptObjectInfo.type != CRYPT_OBJECT_RAW_DATA )
  369.         THROW( exception );
  370.  
  371.     /* Import the raw data object and write as much of it as we have
  372.        available to disk */
  373.     status = cryptImportObject( dataPtr, &payloadStart, &payloadSize );
  374.     if( cryptStatusError( status ) )
  375.         THROW( exception );
  376.     count = ( int ) ( dataInBuffer - signatureSize - payloadStart );
  377.     if( fwrite( dataPtr + payloadStart, 1, count, outFile ) != ( size_t ) count )
  378.         THROW( exception );
  379.  
  380.     /* Now process the input file which contains the payload portion of the
  381.        RawData object.  We just read the file in chunks, decrypt it, and
  382.        write the result to the output file */
  383.     while( dataSize )
  384.         {
  385.         int bufferLength = ( int ) ( ( dataSize > BUFSIZ * 4 ) ? \
  386.                            BUFSIZ * 4 : dataSize );
  387.  
  388.         if( ( bufferLength = fread( ioBuffer, 1, bufferLength, inFile ) ) != bufferLength )
  389.             THROW( exception );
  390.         if( sessionKeyContext )
  391.             cryptDecrypt( sessionKeyContext, ioBuffer, bufferLength );
  392.         if( hashContext )
  393.             cryptDecrypt( hashContext, ioBuffer, bufferLength );
  394.         if( fwrite( ioBuffer, 1, bufferLength, outFile ) != ( size_t ) bufferLength )
  395.             THROW( exception );
  396.         dataSize -= bufferLength;
  397.         }
  398.  
  399.     /* If the data is signed, read the Signature object which follows the
  400.        SignedData object and check the signature */
  401.     if( hashContext )
  402.         {
  403.         CRYPT_KEYSET cryptKeyset;
  404.  
  405.         cryptDecrypt( hashContext, dataPtr, 0 );
  406.  
  407.         /* If we've already got some of the signature in the buffer, move it
  408.            to the start of the buffer so we can read in the rest */
  409.         if( signatureSize )
  410.             memmove( objectBuffer, signature, ( size_t ) signatureSize );
  411.  
  412.         /* Read the Signature object which follows the SignedData object.  We
  413.            may already have some or all of it in memory from previous reads.
  414.            The byte-by-byte read isn't terribly elegant, but much easier than
  415.            a cryptQueryObject() followed by a read of the payload */
  416.         signature = objectBuffer;
  417.         if( !feof( inFile ) )
  418.             {
  419.             int signatureIndex = ( int ) signatureSize;
  420.  
  421.             /* Read in the rest of the signature */
  422.             while( !feof( inFile ) && signatureIndex < 1024 )
  423.                 signature[ signatureIndex++ ] = getc( inFile );
  424.             signatureIndex--;    /* Don't include the EOF */
  425.  
  426.             /* Decrypt the signature or remainder of the signature */
  427.             if( sessionKeyContext )
  428.                 {
  429.                 status = cryptDecrypt( sessionKeyContext,
  430.                                        signature + ( int ) signatureSize,
  431.                                        signatureIndex );
  432.                 if( cryptStatusError( status ) )
  433.                     THROW( exception );
  434.                 }
  435.             }
  436.  
  437.         /* Open the external key collection and try to read the signature key */
  438.         status = cryptExtKeysetOpen( &cryptKeyset, PGP_PUBKEY_FILE,
  439.                                      CRYPT_KEYSET_PGP );
  440.         if( cryptStatusError( status ) )
  441.             THROW( exception );
  442.         status = cryptGetKeyFromObject( cryptKeyset, &signContext,
  443.                                         signature );
  444.         if( cryptStatusError( status ) )
  445.             THROW( exception );
  446.         cryptKeysetClose( cryptKeyset );
  447.  
  448.         /* Check the signature */
  449.         status = cryptCheckSignature( signature, signContext, hashContext );
  450.         cryptDestroyContext( hashContext );
  451.         hashContext = 0;
  452.         cryptDestroyContext( signContext );
  453.         signContext = 0;
  454.         if( cryptStatusError( status ) )
  455.             THROW( exception );
  456.         }
  457.     cryptDestroyContext( sessionKeyContext );
  458.  
  459.     /* Clean up */
  460.     free( objectBuffer );
  461.     return( CRYPT_OK );
  462.  
  463.     /* Exception handlers */
  464. exception:
  465.     if( decryptContext )
  466.         cryptDestroyContext( decryptContext );
  467.     if( signContext )
  468.         cryptDestroyContext( signContext );
  469.     if( hashContext )
  470.         cryptDestroyContext( hashContext );
  471.     if( sessionKeyContext )
  472.         cryptDestroyContext( sessionKeyContext );
  473.     memset( objectBuffer, 0, 4096 );
  474.     free( objectBuffer );
  475.     memset( ioBuffer, 0, 4096 );
  476.     free( ioBuffer );
  477.     return( status );
  478.     }
  479.  
  480. /* Despite the fact that the above is meant only as a cryptlib demo, it's
  481.    probably going to appear in God knows how many pieces of production
  482.    code... */
  483.  
  484. /****************************************************************************
  485. *                                                                            *
  486. *                            Test Routines for File Functions                *
  487. *                                                                            *
  488. ****************************************************************************/
  489.  
  490. /* There are some sizeable (for DOS) data structures used, so we increase the
  491.    stack size to allow for them */
  492.  
  493. #ifdef __MSDOS__
  494.   extern unsigned _stklen = 10240;
  495. #endif /* __MSDOS__ */
  496.  
  497. /* The main program to exercise the encryption code */
  498.  
  499. #if defined( _WINDOWS ) || defined( _WIN32 ) || defined( WIN32 )
  500.   #define __WINDOWS__
  501. #endif /* _WINDOWS || _WIN32 || WIN32 */
  502.  
  503. void getPassword( const char *prompt, char *password )
  504.     {
  505.     printf( "Please enter %s: ", prompt );
  506.     fflush( stdout );
  507.     fgets( password, 200, stdin );
  508.     password[ strlen( password ) - 1 ] = '\0';
  509.     }
  510.  
  511. int main( int argc, char **argv )
  512.     {
  513.     CRYPT_CONTEXT cryptContext = 0, signContext = 0;
  514.     CRYPT_KEYSET cryptKeyset;
  515.     FILE *inFile, *outFile;
  516.     char *encryptID = NULL, *signID = NULL;
  517.     char password[ 200 ];
  518.     int status, encrypt = 1;
  519.  
  520.     /* Process the input parameters */
  521.     if( argc < 3 || argc > 5 )
  522.         {
  523.         puts( "Usage: testapp [-c] [-e<userID>] [-s<userID>] [-d]" );
  524.         puts( "       <infile> <outfile>" );
  525.         puts( "       -c = conventional encrypt" );
  526.         puts( "       -e = encrypt with key for <userID>" );
  527.         puts( "       -d = decrypt and/or check signature" );
  528.         puts( "       -s = sign with key for <userID>" );
  529.         puts( "" );
  530.         puts( "       Example: testapp -c input cipher" );
  531.         puts( "                - Conventionally encrypt input, write result to cipher." );
  532.         puts( "       Example: testapp -eyourkey -smykey input cipher" );
  533.         puts( "                - Encrypt + sign input, write result to cipher." );
  534.         puts( "       Example: testapp -d cipher outfile" );
  535.         puts( "                - Decrypt + sig check prev.file, write result to outfile." );
  536.         return( EXIT_FAILURE );
  537.         }
  538.  
  539.     /* Initialise the library (not necessary for a DLL) */
  540. #ifndef __WINDOWS__
  541.     status = cryptInit();
  542.     if( cryptStatusError( status ) )
  543.         {
  544.         printf( "cryptInit() failed with error code %d.\n", status );
  545.         return( EXIT_FAILURE );
  546.         }
  547. #endif /* !__WINDOWS__ */
  548.  
  549.     /* Check for arguments */
  550.     while( *argv[ 1 ] == '-' )
  551.         {
  552.         char *argPtr = argv[ 1 ] + 1;
  553.  
  554.         switch( toupper( *argPtr ) )
  555.             {
  556.             case 'C':
  557.                 /* Perform basic error checking */
  558.                 if( cryptContext )
  559.                     break;
  560.  
  561.                 /* Get the passphrase and convert it to an encryption key */
  562.                 getPassword( "encryption password", password );
  563.                 cryptCreateContext( &cryptContext, CRYPT_ALGO_3DES,
  564.                                     CRYPT_MODE_CFB );
  565.                 status = cryptDeriveKey( cryptContext, password,
  566.                                          strlen( password ) );
  567.                 memset( password, 0, 200 );
  568.                 if( cryptStatusError( status ) )
  569.                     {
  570.                     printf( "Couldn't build encryption key from password, "
  571.                             "error code %d\n", status );
  572.                     return( EXIT_FAILURE );
  573.                     }
  574.                 break;
  575.  
  576.             case 'E':
  577.                 /* Perform basic error checking */
  578.                 if( cryptContext )
  579.                     break;
  580.                 encryptID = argPtr + 1;
  581.                 if( !strlen( encryptID ) )
  582.                     {
  583.                     puts( "You must specify a user ID to encrypt the file." );
  584.                     return( EXIT_FAILURE );
  585.                     }
  586.  
  587.                 /* Open the external key collection and try to read the
  588.                    required key */
  589.                 status = cryptExtKeysetOpen( &cryptKeyset, PGP_PUBKEY_FILE,
  590.                                              CRYPT_KEYSET_PGP );
  591.                 if( cryptStatusError( status ) )
  592.                     {
  593.                     printf( "Couldn't open public key file %s, error code %d.\n",
  594.                             PGP_PUBKEY_FILE, status );
  595.                     return( EXIT_FAILURE );
  596.                     }
  597.                 status = cryptGetKey( cryptKeyset, &cryptContext, encryptID );
  598.                 if( cryptStatusError( status ) )
  599.                     {
  600.                     printf( "Couldn't get public key for %s, error code %d\n",
  601.                             encryptID, status );
  602.                     return( EXIT_FAILURE );
  603.                     }
  604.                 cryptKeysetClose( cryptKeyset );
  605.                 break;
  606.  
  607.             case 'D':
  608.                 /* Get the private key password.  This may not be necessary
  609.                    if the data isn't encrypted, but at the moment there's no
  610.                    clean mechanism for providing feedback at the unwrapfile
  611.                    level */
  612.                 getPassword( "decryption key password", password );
  613.                 encrypt = 0;
  614.                 break;
  615.  
  616.             case 'S':
  617.                 /* Perform basic error checking */
  618.                 if( signContext )
  619.                     break;
  620.                 signID = argPtr + 1;
  621.                 if( !strlen( signID ) )
  622.                     {
  623.                     puts( "You must specify a user ID to sign the file." );
  624.                     return( EXIT_FAILURE );
  625.                     }
  626.  
  627.                 /* Open the external key collection and try to read the
  628.                    required key */
  629.                 status = cryptExtKeysetOpen( &cryptKeyset, PGP_PRIVKEY_FILE,
  630.                                              CRYPT_KEYSET_PGP );
  631.                 if( cryptStatusError( status ) )
  632.                     {
  633.                     printf( "Couldn't open private key file %s, error code %d.\n",
  634.                             PGP_PRIVKEY_FILE, status );
  635.                     return( EXIT_FAILURE );
  636.                     }
  637.                 status = cryptGetKeyEx( cryptKeyset, &signContext, signID, NULL );
  638.                 if( status == CRYPT_WRONGKEY )
  639.                     {
  640.                     /* We need a password for this private key, get it from
  641.                        the user and get the key again */
  642.                     getPassword( "signature key password", password );
  643.                     status = cryptGetKeyEx( cryptKeyset, &signContext,
  644.                                             signID, password );
  645.                     }
  646.                 memset( password, 0, 200 );
  647.                 if( cryptStatusError( status ) )
  648.                     {
  649.                     printf( "Couldn't get private key for %s, error code %d\n",
  650.                             signID, status );
  651.                     return( EXIT_FAILURE );
  652.                     }
  653.                 cryptKeysetClose( cryptKeyset );
  654.                 break;
  655.  
  656.             case 'T':
  657.                 /* Perform basic error checking */
  658.                 if( cryptContext )
  659.                     break;
  660.                 encryptID = argPtr + 1;
  661.                 if( !strlen( encryptID ) )
  662.                     {
  663.                     puts( "You must specify a user ID for the test key." );
  664.                     return( EXIT_FAILURE );
  665.                     }
  666.  
  667.                 /* Undocumented test mode to check readability of various
  668.                    unusual X.509-related key file formats */
  669. #ifdef TEST_PUB
  670.                 status = cryptExtKeysetOpen( &cryptKeyset, X509_PUBKEY_FILE,
  671.                                              CRYPT_KEYSET_X509 );
  672.                 if( cryptStatusError( status ) )
  673.                     {
  674.                     printf( "Couldn't open public key file %s, error code %d.\n",
  675.                             X509_PUBKEY_FILE, status );
  676.                     return( EXIT_FAILURE );
  677.                     }
  678.                 status = cryptGetKey( cryptKeyset, &cryptContext, encryptID );
  679.                 if( cryptStatusError( status ) )
  680.                     {
  681.                     printf( "Couldn't get public key for %s, error code %d\n",
  682.                             encryptID, status );
  683.                     return( EXIT_FAILURE );
  684.                     }
  685. #else
  686.                 status = cryptExtKeysetOpen( &cryptKeyset, X509_PRIVKEY_FILE,
  687.                                              CRYPT_KEYSET_X509 );
  688.                 if( cryptStatusError( status ) )
  689.                     {
  690.                     printf( "Couldn't open private key file %s, error code %d.\n",
  691.                             X509_PRIVKEY_FILE, status );
  692.                     return( EXIT_FAILURE );
  693.                     }
  694.                 status = cryptGetKeyEx( cryptKeyset, &cryptContext, encryptID,
  695.                                         NULL );
  696.                 if( cryptStatusError( status ) )
  697.                     {
  698.                     printf( "Couldn't get private key for %s, error code %d\n",
  699.                             encryptID, status );
  700.                     return( EXIT_FAILURE );
  701.                     }
  702. #endif /* TEST_PUB */
  703.                 cryptKeysetClose( cryptKeyset );
  704.                 break;
  705.  
  706.             default:
  707.                 printf( "Unknown arg '%c'.\n", argPtr );
  708.                 return( EXIT_FAILURE );
  709.             }
  710.  
  711.         argv++;
  712.         }
  713.  
  714.     /* Open the input and output files */
  715.     if( ( inFile = fopen( argv[ 1 ], "rb" ) ) == NULL )
  716.         {
  717.         perror( argv[ 1 ] );
  718.         return( EXIT_FAILURE );
  719.         }
  720.     if( ( outFile = fopen( argv[ 2 ], "wb" ) ) == NULL )
  721.         {
  722.         fclose( inFile );
  723.         perror( argv[ 2 ] );
  724.         return( EXIT_FAILURE );
  725.         }
  726.  
  727.     /* In order to avoid having to do a randomness poll for every test run,
  728.        we bypass the randomness-handling by adding some junk - it doesn't
  729.        matter here because we're not worried about security, but should never
  730.        be done in production code */
  731.     cryptAddRandom( "a", 1 );
  732.  
  733.     /* Encrypt or decrypt the data */
  734.     if( encrypt )
  735.         /* Wrap up the data */
  736. #ifdef USE_ENVELOPES
  737.         {
  738.         cryptCreateEnvelope( &cryptEnvelope, fsize( inFile ), 16384 );
  739.         if( signContext )
  740.             cryptAddKey( cryptEnvelope, CRYPT_ENVKEY_SIGNATURE, signKey );
  741.         if( cryptContext )
  742.             cryptAddKey( cryptEnvelope, CRYPT_ENVKEY_PKCKEY, cryptContext );
  743.         while( !feof( inFile ) )
  744.             {
  745.             if( ( bufferLength = fread( buffer, 1, 16384, inFile ) ) == 0 )
  746.                 break;
  747.             cryptEnvelopePush( cryptEnvelope, buffer, bufferLength );
  748.             cryptEnvelopePop( cryptEnvelope, buffer, bufferLength );
  749.             fwrite( buffer, 1, bufferLength, outFile );
  750.             }
  751.         cryptDestroyEnvelope( cryptEnvelope );
  752.         }
  753. #else
  754.         status = wrapFile( inFile, outFile, signContext, cryptContext,
  755.                            CRYPT_ALGO_3DES );
  756. #endif /* USE_ENVELOPES */
  757.     else
  758.         /* Unwrap the data */
  759.         status = unwrapFile( inFile, outFile, password );
  760.  
  761.     /* Clean up */
  762.     if( cryptContext )
  763.         cryptDestroyContext( cryptContext );
  764.     if( signContext )
  765.         cryptDestroyContext( signContext );
  766.     fclose( inFile );
  767.     fclose( outFile );
  768.     if( cryptStatusError( status ) )
  769.         {
  770.         printf( "%s failed with error code %d\n", ( encrypt ) ? \
  771.                 "wrapFile()" : "unwrapFile()", status );
  772.         return( EXIT_FAILURE );
  773.         }
  774.  
  775.     /* Clean up */
  776. #ifndef __WINDOWS__
  777.     status = cryptEnd();
  778.     if( cryptStatusError( status ) )
  779.         {
  780.         printf( "cryptEnd() failed with error code %d.\n", status );
  781.         return( EXIT_FAILURE );
  782.         }
  783. #endif /* !__WINDOWS__ */
  784.     if( encrypt )
  785.         {
  786.         if( signID )
  787.             printf( "File was signed by %s.\n", signID );
  788.         if( encryptID )
  789.             printf( "File was encrypted for %s.\n", encryptID );
  790.         if( !signID && !encryptID )
  791.             puts( "File was encapsulated as raw data." );
  792.         }
  793.     else
  794.         printf( "File decryption succeeded.\n" );
  795.     return( EXIT_SUCCESS );
  796.     }
  797.