RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

cm_sign_dgst.c

/* $Id: cm_sign_dgst.c,v 1.10 2005/08/08 05:33:30 jlevander Exp $ */
/*
 * Copyright (C) 1998-2003 RSA Security Inc.
 *
 * This file shall only be used to demonstrate how to interface to an
 * RSA Security Inc. licensed development product.
 *
 * You have a royalty-free right to use, reproduce and distribute this
 * demonstration file, provided that you agree that RSA Security Inc.
 * has no warranty, implied or otherwise, or liability for this
 * demonstration file (including any modified version).  This software
 * is provided "as is" without warranties or representations of any
 * kind. RSA Security disclaims all conditions and warranties, statutory
 * and otherwise, both express and implied, with respect to the software,
 * its quality and performance, including but not limited to, all
 * implied warranties of merchantability, fitness for a particular
 * purpose, title and noninfringement of third party rights. Without
 * limiting the foregoing, RSA Security does not warrant that the
 * software is error-free or that errors in the product will be
 * corrected. You agree that RSA Security shall not be liable for any
 * direct, indirect, incidental, special, consequential, punitive or
 * other damages whatsoever resulting from your use of this software
 * or any modified version.
 *
 *
 */

/*
 * @file cm_sign_dgst.c
 * This sample demonstrates how to create a PKCS #7 signed data message
 * with detached data when the data is streamed.
 *
 * The data to sign is provided in a file and specified by the -data
 * option. The file may contain plain data or the data may be
 * wrapped. Where the data is wrapped the -wrapped switch must be used.
 *
 * The data may be signed by a number of signers. For each signer the
 * private key, certificate and digest must be specified. In the case of
 * multiple signers, the information is colon separated in a continuous
 * string (see examples that follow). The order of the keys, certificates and
 * digests specified must be consistent. That is, the second key in the
 * list must correspond to the second certificate and digest.
 *
 * For example, to:
 *
 * Sign the data contained within the PKCS #7 signed data message:
 *   cm_sign_dgst -out pkcs7.sd -data data.txt
 *           -keys a.key -sign_certs a.cert -digests MD5
 *
 * Sign the data within the PKCS #7 message - input data is wrapped:
 *   cm_sign_dgst -out pkcs7.sd -data data.txt -wrapped
 *           -keys a.key -sign_certs a.cert -digests MD5
 *
 * Sign data within PKCS #7 message - two signers:
 *   cm_sign_dgst -out pkcs7.sd -data data.txt
 *           -keys a.key:b.key -sign_certs a.cert:b.cert -digests MD5:SHA1
 *
 * where: pkcs7.sd    = Output file where the PKCS #7 signed data is written.
 *        data.txt    = The text file that has the data to sign.
 *        a.cert      = The signer's certificate.
 *        a.key       = The private key of the signer.
 */

#include "r_prod.h"
#include "cm_com.h"
#include "r_cm.h"

/* Usage help message */
static char *cm_sign_dgst_usage[] =
{
    "usage: cm_sign_dgst [options]\n",
    "where options are:\n",
    " -out file            - The file containing the cryptographic message\n",
    " -data file           - The file containing the data to sign\n",
    " -wrapped             - The data in the input data file is wrapped\n",
    " -wrap                - The data when written out is to be wrapped\n",
    " -keys list           - A list of private keys to sign with (colon\n",
    "                        separated)\n",
    " -keytype encoding    - Encode the keys - one of RSA (default)\n",
#ifdef NO_PEM
    " -keyform format      - Format of the keys (BIN only)\n",
#else
    " -keyform format      - Format of the keys - one of BIN (default), PEM\n",
#endif /* NO_PEM */
    " -digests list        - Digests to use for each key (colon separated),\n"
    "                        default is MD5.\n",
    " -sign_certs list     - A list of certificates matching private keys\n",
    " -certs list          - A list of Certification Authority (CA)\n",
    "                        certificates to add (colon separated)\n",
    " -certtype encoding   - Encoding of the certificates - only X509\n",
    "                        (default) supported\n",
#ifdef NO_PEM
    " -certform format     - Format the certificates (BIN only)\n",
#else
    " -certform format     - Format the certificates\n",
    "                        - one of BIN (default), PEM\n",
#endif /* NO_PEM */
    " -no_auth             - Do not add digest to the authenticated\n",
    "                        attributes\n",
    " -no_cert             - Do not add the signer's certificate to the\n",
    "                        message\n",
    " -print_signer        - Print the certificate information for each of\n",
    "                        the signers in the cryptographic message\n",
    " -print_data          - Print the signed data of the cryptographic\n",
    "                        message\n",
#ifdef NO_SOFTWARE_CRYPTO
    " -no_fips140          - Use non FIPS140 crypto implementations\n",
#endif /* NO_SOFTWARE_CRYPTO */
    " -eg                  - Print example usages for this utility\n",
    NULL
};

static char *cm_sign_dgst_example_usage[] =
{
    "Signed data contained within the PKCS7 signed data message:\n",
    "cm_sign_dgst -out pkcs7.sd -data data.txt \n",
    "        -keys a.key -sign_certs a.cert -digests MD5\n",
    "\n",
    "Signed data within the PKCS7 message - input data is wrapped.\n",
    "cm_sign_dgst -out pkcs7.sd -data data.txt -wrapped\n",
    "        -keys a.key -sign_certs a.cert -digests MD5\n",
    "\n",
    "Signed data within the PKCS7 message - 2 signers!\n",
    "  cm_sign_dgst -out pkcs7.sd -data data.txt\n",
    "         -keys a.key:b.key -sign_certs a.cert:b.cert -digests MD5:SHA1\n",
    "\n",
    "Signed data is detached from the PKCS7 message. The data is appended\n",
    "to the PKCS7 message in the same file.\n",
    "  cm_sign_dgst -out pkcs7.sd -data data.txt \n",
    "         -keys a.key -sign_certs a.cert -digests MD5 -detached -single\n",
    "\n",
    "where: pkcs7.sd = Output file where the PKCS #7 signed data is written\n",
    "       data.txt = The text file that has the data to sign\n",
    "       a.cert   = The signer's certificate\n",
    "       a.key    = The private key of the signer\n",
    NULL
};

/* The number of data Bytes to read */
#define BUF_LEN  1024
/* The maximum number of different digests */
#define MAX_NUM_DIGESTS 10

/*
 * Main sample program entry point.
 *
 * @param argc  [In]  The number of arguments typed on the command line.
 * @param argv  [In]  The array of individual arguments from the command line.
 *
 * @returns  R_ERROR_NONE indicates success.
 *           See @ref R_ERROR_IDS for valid values.
 */
int main(int argc, char **argv)
{
    int               ret       = R_ERROR_NONE;
    BIO              *bio_out   = NULL;
    BIO              *bio_in   = NULL;
    BIO              *bio_out_raw   = NULL;
    BIO              *bio_err   = NULL;
    BIO              *bio_file  = NULL;
    BIO              *bio_digest  = NULL;
    BIO              *bio_tos  = NULL;

    R_RES_LIST       *res_list  = NULL;
    R_LIB_CTX        *lib_ctx   = NULL;
    R_CM_CTX         *ctx       = NULL;
    R_CM             *obj       = NULL;
    R_CM             *data_obj  = NULL;
    R_CERT_CTX       *cert_ctx  = NULL;
    R_PKEY_CTX       *key_ctx   = NULL;
    char             *cm_file;
    char             *datafile;
    char             *certfile;
    char             *signcertfile;
    char             *dgstname = "MD5";
    char             *dgsts;
    char             *dgst_mem_ptr   = NULL;
    char             *keyfile;
    char             *str;
    R_CERT_TYPE       certtype;
    R_FORMAT          certform;
    R_PKEY_TYPE       keytype;
    R_FORMAT          keyform;
    int               detached;
    int               wrap;
    int               wrapped;
    int               single;
    int               print_sig;
    int               print_data;
    int               add_cert;
    int               add_auth;
    R_CM_ENCODING     format;
    int               data_len;
    int               len;
    int               digest_num;
    int               lastest_idx;
    unsigned char    *data      = NULL;
    char              raw_file[]="raw.out";
    int               i;
    R_INDEXED_INFO    digest_info = {0, 0, 0, R_CR_ID_SHA1};
    R_INDEXED_INFO    digest_data = {0,0,0,0};
    int               id = R_CR_ID_SHA1;
    int               digest_id[MAX_NUM_DIGESTS];
    BIO              *digest_bios[MAX_NUM_DIGESTS];
    char             *dgst_str = NULL;
    int               dgst_len;

    /* Set the defaults */
    cm_file      = NULL;
    datafile     = NULL;
    certfile     = NULL;
    signcertfile = NULL;
    keyfile      = NULL;
    certtype     = R_CERT_TYPE_X509;
    certform     = R_FORMAT_BINARY;
    keytype      = R_PKEY_TYPE_RSA;
    keyform      = R_FORMAT_BINARY;
    print_sig    = 0;
    print_data   = 0;
    detached     = 0;
    wrap         = 0;
    wrapped      = 0;
    single       = 0;
    add_cert     = 1;
    add_auth     = 1;
    digest_num   = 0;

    Memset(digest_id, 0, sizeof(int) * MAX_NUM_DIGESTS);

    res_list = PRODUCT_DEFAULT_RESOURCE_LIST();

    /*
     * Create BIOs to stderr and stdout. BIOs are the Basic Input/Output
     * mechanism provided by RSA and are recommended for all input and output
     * from applications.
     */
    bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
    bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);

    if ((bio_err == NULL) || (bio_out == NULL))
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto err;
    }

    BIO_set_flags(bio_err, BIO_FLAGS_FLUSH_ON_WRITE);
    BIO_set_flags(bio_out, BIO_FLAGS_FLUSH_ON_WRITE);

    /* Parse the command line parameters */
    /* Skip the program name */
    argc--;
    argv++;

    /* Process all command line options */
    while (argc >= 1)
    {
        if (Strcmp(*argv, "-out") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            cm_file = *(++argv);
        }
        else if (Strcmp(*argv, "-data") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            datafile = *(++argv);
        }
        else if (Strcmp(*argv, "-wrap") == 0)
        {
            wrap = 1;
        }
        else if (Strcmp(*argv, "-wrapped") == 0)
        {
            wrapped = 1;
        }
        else if (Strcmp(*argv, "-keys") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            keyfile = *(++argv);
        }
        else if (Strcmp(*argv, "-keytype") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }

            str = *(++argv);

            if ((ret = R_PKEY_TYPE_from_string(&keytype, str)) != R_ERROR_NONE)
            {
                BIO_printf(bio_out, "Unknown key type %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-keyform") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }

            str = *(++argv);

            if ((ret = R_FORMAT_from_string(str, &keyform)) != R_ERROR_NONE)
            {
                BIO_printf(bio_out, "Unknown key format %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-digests") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            dgstname = *(++argv);
        }
        else if (Strcmp(*argv, "-sign_certs") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            signcertfile = *(++argv);
        }
        else if (Strcmp(*argv, "-certs") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            certfile = *(++argv);
        }
        else if (Strcmp(*argv, "-certtype") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }

            str = *(++argv);

            if ((ret = R_CERT_TYPE_from_string(str, &certtype)) != R_ERROR_NONE)
            {
                BIO_printf(bio_out, "Unknown certificate type %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-certform") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }

            str = *(++argv);

            if ((ret = R_FORMAT_from_string(str, &certform)) != R_ERROR_NONE)
            {
                BIO_printf(bio_out, "Unknown certificate format %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-no_auth") == 0)
        {
            add_auth = 0;
        }
        else if (Strcmp(*argv, "-no_cert") == 0)
        {
            add_cert = 0;
        }
        else if (Strcmp(*argv, "-print_signer") == 0)
        {
            print_sig = 1;
        }
        else if (Strcmp(*argv, "-print_data") == 0)
        {
            print_data = 1;
        }
#ifdef NO_SOFTWARE_CRYPTO
        else if (Strcmp(*argv, "-no_fips140") == 0)
        {
            res_list = PRODUCT_NON_FIPS_140_MODE_RESOURCE_LIST();
        }
#endif /* NO_SOFTWARE_CRYPTO */
        else if (Strcmp(*argv,"-eg") == 0)
        {
            char **egp;
            for (egp = cm_sign_dgst_example_usage; (*egp) != NULL; egp++)
            {
                BIO_printf(bio_out, *egp);
            }
            goto err;
        }
        else
        {
            BIO_printf(bio_err, "Unknown option %s\n", *argv);
            goto bad;
        }
        argc--;
        argv++;
    }

    /* Simple checks first */
    if (datafile == NULL)
    {
        BIO_printf(bio_err, "Message data required\n");
        goto bad;
    }

    /* Display the help menu if an invalid command line option was entered */
    if (0)
    {
        char **pp;
bad:
        for (pp = cm_sign_dgst_usage; (*pp != NULL); pp++)
        {
            BIO_printf(bio_err, *pp);
        }
        goto err;
    }

    /*
     * Create a library context to provide access to all configurable aspects
     * of the library
     */
    if ((ret = PRODUCT_LIBRARY_NEW(res_list, R_RES_FLAG_DEF, &lib_ctx)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Library new failure\n");
        goto err;
    }

    /*
     * Create the key, certificate, and cryptographic message contexts.
     * These contexts are required if any R_CERT_*, R_PKEY_*, or R_CM_*
     * routines are used.
     */

    /* Create a new key context */
    if ((ret = R_PKEY_CTX_new(lib_ctx, R_RES_FLAG_DEF, keytype, &key_ctx)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Key context new failure\n");
        goto err;
    }

    /* Create a new certificate context */
    if ((ret = R_CERT_CTX_new(lib_ctx, R_RES_FLAG_DEF, certtype, &cert_ctx)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Certificate context new failure\n");
        goto err;
    }

    /* Create a new cryptographic message context */
    if ((ret = R_CM_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_CM_TYPE_DEFAULT,
        &ctx)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "R_CM_CTX_new failure\n");
        goto err;
    }

    /*
     * Create the required BIOs:
     *   - bio_out_raw: The raw streamed data to file in this case
     *   - bio_digest:  One for each digest.
     * These are pushed onto the bio_out_raw to create a BIO stack.
     */
    /* Open the output BIO - this is where the raw stream data is output */
    if (raw_file == NULL)
    {
        bio_out_raw = bio_out;
        BIO_set_flags(bio_out, BIO_FLAGS_FLUSH_ON_WRITE);
    }
    else if ((bio_out_raw = BIO_new_file(raw_file, "wb")) == NULL)
    {
        BIO_printf(bio_err, "Could not open file: %s\n", raw_file);
        ret = R_ERROR_FAILED;
        goto err;
    }

    /* bio_tos points to the bio at the top of the stack */
    bio_tos = bio_out_raw;

    /* Copy the digest string as it will be used twice */
    if ((dgsts = (char *)Malloc(Strlen(dgstname) + 1)) == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto err;
    }
    Strcpy(dgsts, dgstname);
    dgst_mem_ptr = dgsts;

    /*
     * Iterate through the list of digests and create BIO filters for each
     * digest type (only one for each).
     */
    dgst_len = R_STR_token(&dgsts, ':', NULL);

    while (dgst_len > 0)
    {
        if ((dgst_str = (char *)Malloc(dgst_len + 1)) == NULL)
        {
            ret = R_ERROR_ALLOC_FAILURE;
            goto err;
        }

        R_STR_token(&dgsts, ':', dgst_str);

        /* Retrieve the digest algorithm from the string */
        if ((ret = R_CR_ID_from_string(dgst_str, &id)) != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Bad algorithm identifier: %s\n", dgst_str);
            goto err;
        }

        /* Check if it is a new digest */
        for (i=0; i<= digest_num; i++ )
          if(id == digest_id[i]) goto end_loop;

        /* Create digest BIO and add it to the stack */
        /* Create a new cryptographic BIO filter */
        if (BIO_new_init(lib_ctx, 0, BIO_TYPE_CR,
            R_CR_TYPE_DIGEST, &id, &bio_digest) != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Failed create new digest BIO\n");
            goto err;
        }

        BIO_push(bio_digest, bio_tos);
        bio_tos = bio_digest;
        /*
         * Keep track of all the unique digests as each one will need to be
         * added to the R_CM object before signing and creating the
         * PKCS #7 message
         */
        digest_id[digest_num] = id;
        digest_bios[digest_num++] = bio_digest;

end_loop:
        /* Free the allocated strings */
        Free(dgst_str);
        dgst_str = NULL;
        dgst_len = R_STR_token(&dgsts, ':', NULL);
    }


    /*
     * Stream in the data to sign. The data will need to be read from file and
     * written into the top of the BIO filter stack.
     */
    format = R_CM_ENCODING_FORMAT_RAW;
    if (wrapped == 1)
    {
        format = R_CM_ENCODING_FORMAT_WRAPPED;
    }

    /* Open a BIO to read the message data */
    if ((bio_in = BIO_new_file(datafile, "rb")) == NULL)
    {
        BIO_printf(bio_err, "Could not open: %s\nNo data to sign.\n", datafile);
        ret = R_ERROR_FAILED;
        goto err;
    }

    /* Create a buffer to write data into */
    data = (unsigned char *)Malloc(BUF_LEN);
    if (data == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto err;
    }

    while ((data_len = BIO_read(bio_in, (char *)(data), BUF_LEN)) != 0)
    {
        char *p;

        /* If the read is to be retried there will be nothing to write */
        if (data_len == -1)
        {
            if (BIO_should_retry(bio_in))
            {
                continue;
            }
            else
            {
                BIO_printf(bio_err, "Fatal error while reading\n" );
                ret = R_ERROR_IO;
                goto err;
            }
        }

        p = (char *)data;

        /*
         * Write out the data in the data message digest BIO stack.
         * Keep trying while the error is retried.
         */
        do
        {
            len = BIO_write(bio_digest, p, data_len);
            if (len > 0)
            {
                data_len -= len;
                p += len;
            }
            else if ((len < 0) && !BIO_should_retry(bio_digest))
            {
                break;
            }
        }
        while (data_len > 0);

        if (data_len > 0)
        {
            BIO_printf(bio_err, "Failed to write out data\n");
            ret = R_ERROR_IO;
            goto err;
        }
    }

    /*
     * Create and configure the signed data object. The signed data message
     * will eventually hold all of the information for the cryptographic
     * message. Set up the information required to construct the cryptographic
     * message:
     *     - Signer certificates.
     *     - Extra certificates to include in the cryptographic message
     *       (optional).
     *     - Digests of the streamed data.
     *     - Flags to indicate what certificates to include in the
     *       cryptographic message.
     */
    /* Create a new signed data message */
    if ((ret = R_CM_new(ctx, R_CM_TYPE_SIGNED_DATA, &obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "R_CM_new failure (Signed Data)\n");
        goto err;
    }

    /* Add all digests to the R_CM object */
    for( i=0; i<digest_num; i++ )
    {
      /* Get the digest from the BIO */
      data_len = BUF_LEN;
      data_len = BIO_gets(digest_bios[i], (char *)data, data_len);

      digest_info.value = digest_id[i];
      R_CM_set_info(obj, R_CM_INFO_DIGEST_ALG, &digest_info);
      lastest_idx = digest_info.index;

      digest_data.index = lastest_idx;
      digest_data.data = data;
      digest_data.len = data_len;
      R_CM_set_info(obj, R_CM_INFO_DIGEST, &digest_data);
    }

    /* Add extra certificates to the signed data message */
    if ((ret = add_certs(bio_err, cert_ctx, obj, certfile, certtype,
        certform)) != R_ERROR_NONE)
    {
        goto err;
    }

    /* Add all signers to the signed data message */
    if ((ret = add_signers(bio_err, cert_ctx, key_ctx, obj, signcertfile,
        certtype, certform, keyfile, keytype, keyform, dgstname)) !=
        R_ERROR_NONE)
    {
        goto err;
    }

    /* Configure what needs to be added to the signed data message */
    R_CM_set_info(obj, R_CM_INFO_ADD_SIGNER_CERT, &add_cert);
    R_CM_set_info(obj, R_CM_INFO_ADD_AUTH_DIGEST, &add_auth);

    /* Sign the data */
    if ((ret = R_CM_sign(obj, R_CM_INDEX_ALL)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to sign\n");
        goto err;
    }

    /*
     * Print the data. Display the signer details if requested.
     */
    if (print_sig == 1)
    {
        /*
         * Print the signed data message. If print_data != 0 then the
         * data will also be printed to bio_out.
         */
        if ((ret = R_CM_write(obj, bio_out, R_FORMAT_TEXT, &print_data,
            R_CM_ENCODING_FORMAT_WRAPPED)) != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Write signedData data failure\n");
            goto err;
        }
    }

    /* Output the cryptographic message */
    if (cm_file != NULL)
    {
        /* Open the output BIO */
        if ((bio_file = BIO_new_file(cm_file, "wb")) == NULL)
        {
            BIO_printf(bio_err, "Failed to open file: %s\n", cm_file);
            ret = R_ERROR_ALLOC_FAILURE;
            goto err;
        }

        /* Select the right formatting for the signed data message */
        format = R_CM_ENCODING_FORMAT_WRAPPED;

        if (detached != 0)
        {
            format |= R_CM_ENCODING_FORMAT_DETACHED_DATA;
        }

        /* Write out the signed data message */
        if ((ret = R_CM_write(obj, bio_file, R_FORMAT_BINARY, NULL, format)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Failed to write signedData to file\n");
            goto err;
        }

        /*
         * Append the data message if the data is detached and it is to be
         * appended at the end of the signed data
         */
        if ((detached != 0) && (single != 0))
        {
            /* Write out data message in the format required */
            if (wrap != 0)
            {
                format = R_CM_ENCODING_FORMAT_WRAPPED;
            }
            else
            {
                format = R_CM_ENCODING_FORMAT_RAW;
            }

            if ((ret = R_CM_write(data_obj, bio_file, R_FORMAT_BINARY, NULL,
                format)) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Failed to write the appended data\n");
            }
        }
    }

err:

    /*
     * Clean up. Report errors if there is an output stream using both the
     * error and the string representation. Destroy the dynamically allocated
     * objects and return an exit code.
     */
    if ((ret != R_ERROR_NONE) && (bio_err != NULL))
    {
        BIO_printf(bio_err, "ERROR: (%d) %s\n", ret,
            R_LIB_CTX_get_error_string(lib_ctx, R_RES_MOD_ID_LIBRARY, ret));
    }

    if (obj != NULL)
    {
        R_CM_free(obj);
    }
    if (data != NULL)
    {
        Free(data);
    }
    if (dgst_mem_ptr != NULL)
    {
        Free(dgst_mem_ptr);
    }
    if (data_obj != NULL)
    {
        R_CM_free(data_obj);
    }
    if (ctx != NULL)
    {
        R_CM_CTX_free(ctx);
    }
    if (cert_ctx != NULL)
    {
        R_CERT_CTX_free(cert_ctx);
    }
    if (key_ctx != NULL)
    {
        R_PKEY_CTX_free(key_ctx);
    }
    if (bio_file != NULL)
    {
        BIO_free(bio_file);
    }
    for(i=0;i<digest_num;i++)
    {
      if (digest_bios[i] != NULL)
      {
          BIO_free(digest_bios[i]);
      }
    }
    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }
    if (bio_err != NULL)
    {
        BIO_free(bio_err);
    }
    if (bio_in != NULL)
    {
        BIO_free(bio_in);
    }
    if (bio_out_raw != NULL)
    {
        BIO_free(bio_out_raw);
    }
    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }

    return(R_ERROR_EXIT_CODE(ret));
}

Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 072-001001-2100-001-000 - 2.1