RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

cm.c

/* $Id: cm.c,v 1.43.2.1 2005/11/03 23:15:05 gsingh 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.c
 * This sample demonstrates operations with PKCS #7 signed data messages.
 * The operations include verifying the signed data, optionally verifying the
 * signer's certificate, and printing the data and PKCS #7 message components.
 *
 * In its simplest form this sample will verify the signature. Optional
 * switches can be supplied to print the data and signer information from the
 * signed data message. Verification of the signer certificate can also be
 * executed.
 *
 * For information detailing generating PKCS #7 signed messages,
 * see cm_sign.c.
 *
 * For example, to:
 *
 * Verify the signature and print the data:
 *   cm -cm_msg signed.data -print_data
 *
 * Verify the signature - certificate not in PKCS7 message
 * (supply signer certificate):
 *   cm -cm_msg signed.data -certs signer.cert -print_signers -print_data
 *
 * Verify the signature and signer certificate (verify cert chain to
 * Certification Authority (CA) certificate):
 *   cm -cm_msg signed.data -certs issuer.cert -print_data -vfy_opts ALL
 *
 * where: signed.data = output file where pkcs7 data is written
 *        signer.cert = The signer's certificate (used when the signed data
 *                      was created).
 *        issuer.cert = The trusted issuer of the signer's certificate.
 *
 */

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

/* Usage help message. */
static char *cm_usage[] =
{
    "usage: cm [options]\n",
    "where options are:\n",
    " -cm_msg file          - The file containing the cryptographic message\n",
    " -data_msg file        - The file containing the data if it's not part\n",
    "                         of the cryptographic message\n",
    " -single               - The cryptographic message and detached data\n",
    "                         are in the same file, the cryptographic message "
    "first\n",
    "                         followed by the detached data\n",
    " -wrap                 - The detached message is wrapped\n",
    "                         structure and should be unwrapped before use\n",
    " -certs list           - A list of certificates (colon separated)\n",
    " -certtype encoding    - Encoding the certificates - only X509\n",
    "                         (default) supported\n",
#ifdef NO_PEM
    " -certform format      - The format of the certificates (BIN only)\n",
#else
    " -certform format      - The format of the certificates - one of BIN "
    "(default),\n",
    "                         PEM\n",
#endif /* NO_PEM */
    " -vfy_opts option      - The certificate verification options",
    " - one of NONE,\n",
    "                         DEF, ALL, NO_COMPLETE\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",
    " message\n",
    " -print_certs          - Print the certificates contained in the",
    " message\n",
    " -time YYYYMMDDHHMMSSZ - Set the verification time rather than use the\n",
    "                         system time\n",
#ifdef NO_SOFTWARE_CRYPTO
    " -no_fips140           - Use non FIPS140 crypto implementations\n",
#endif /* NO_SOFTWARE_CRYPTO */
    " -eg                   - Print some example usages.\n",
    NULL
};

static char *cm_example_usage[] =
{
    "Verify the signature and print the data:\n",
    "cm -cm_msg signed.data -print_data\n",
    "\n",
    "Verify the signature - certificate not in PKCS7 message",
    " (supply signer cert)\n",
    "cm -cm_msg signed.data -certs signer.cert -print_signers -print_data\n",
    "\n",
    "Verify the signature and signer certificate",
    " (verify cert chain to CA cert)\n",
    "cm -cm_msg signed.data -certs issuer.cert -print_data -vfy_opts ALL\n",
    "\n",
    "  where: signed.data = output file where pkcs7 data is written\n",
    "         signer.cert = The signer's certificate",
    " (used when the signed data\n",
    "                       was created).\n",
    "         issuer.cert = The trusted issuer of the signer's certificate.\n",
    "\n",
    NULL
};

/*
 * 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)
{
    R_RES_LIST       *res_list;
    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_CERT_STORE_CTX *store_ctx = NULL;
    R_VERIFY_CTX     *vfy_ctx   = NULL;
    BIO              *bio_out   = NULL;
    R_ITEM            data      = { 0, NULL };
    R_TITEM           msg_data  = { R_FLAG_SHARE_DATA, 0, NULL };
    char             *cm_file;
    char             *datafile;
    char             *certfile;
    char             *certtype;
    char             *certform;
    char             *options;
    unsigned char    *cm_data   = NULL;
    unsigned int      num_used;
    unsigned int      cm_len;
    int               ret       = R_ERROR_NONE;
    int               is_verified;
    R_CM_TYPE         data_type;
    int               wrap;
    int               single;
    int               print_sig;
    int               print_data;
    unsigned int      slen;
    char             *vfy_time = NULL;
    int               print_certs;
    int               num_signers = 0;

    /* Set the defaults */
    cm_file     = NULL;
    datafile    = NULL;
    certfile    = NULL;
    certtype    = "X509";
    certform    = "BIN";
    options     = NULL;
    print_sig   = 0;
    print_data  = 0;
    print_certs = 0;
    single      = 0;
    wrap        = 0;

    res_list = PRODUCT_DEFAULT_RESOURCE_LIST();

    /*
     * Create a BIO to stdout. BIOs are the Basic Input/Output mechanism
     * provided by RSA and are recommended for all input and output from
     * applications.
     */
    if ((bio_out = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL)
    {
        ret = R_ERROR_ALLOC_FAILURE;
        goto end;
    }

    /* Skip the program name */
    argc--;
    argv++;

    /* Parse the command line parameters */
    while (argc >= 1)
    {
        if (Strcmp(*argv, "-cm_msg") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            cm_file = *(++argv);
        }
        else if (Strcmp(*argv, "-data_msg") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            datafile = *(++argv);
        }
        else if (Strcmp(*argv, "-single") == 0)
        {
            single = 1;
        }
        else if (Strcmp(*argv, "-wrap") == 0)
        {
            wrap = 1;
        }
        else if (Strcmp(*argv, "-certs") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            certfile = *(++argv);
        }
        else if (Strcmp(*argv, "-certtype") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            certtype = *(++argv);
        }
        else if (Strcmp(*argv, "-certform") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            certform = *(++argv);
        }
        else if (Strcmp(*argv, "-vfy_opts")  == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            options = *(++argv);
        }
        else if (Strcmp(*argv, "-print_signer") == 0)
        {
            print_sig = 1;
        }
        else if (Strcmp(*argv, "-print_certs") == 0)
        {
            print_certs = 1;
        }
        else if (Strcmp(*argv, "-print_data") == 0)
        {
            print_data = 1;
        }
        else if (Strcmp(*argv,"-eg") == 0)
        {
            char **egp;
            for (egp = cm_example_usage; (*egp) != NULL; egp++)
            {
                BIO_printf(bio_out, *egp);
            }
            goto end;
        }
        else if (Strcmp(*argv, "-time") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            vfy_time = *(++argv);
        }
#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
        {
            BIO_printf(bio_out, "Unknown option %s\n", *argv);
            goto bad;
        }
        argc--;
        argv++;
    }

    /* Simple checks first */
    if (cm_file == NULL)
    {
        BIO_printf(bio_out, "Input file required\n");
        goto bad;
    }

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

    /*
     * Create the library context. Retrieve the default resource list 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_out, "Library new failure\n");
        goto end;
    }

    /*
     * Set up the certificate related functionality.
     * If any certificates are specified:
     *    - Create a certificate context.
     *    - Create a store context.
     *    - Create a store object.
     * For each certificate:
     *    - Read the certificate from file.
     *    - Set the certificate against the store object.
     *    - Set the certificate trust level.
     *    - Add the certificate to the store (using the store object).
     */
    if ((ret = load_certificates(bio_out, lib_ctx, certfile, certtype, certform,
        &cert_ctx, &store_ctx)) != R_ERROR_NONE)
    {
        goto end;
    }


    /* Create a verification context and set the verification options */
    if ((ret = set_verification(bio_out, lib_ctx, options, vfy_time, &vfy_ctx))
        != R_ERROR_NONE)
    {
        goto end;
    }

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

    /*
     * Read the data from cryptographic message file into the buffer. The
     * cryptographic message is in binary form and simply copied to a
     * buffer pointed to by cm_data.
     */
    if ((ret = data_from_file(bio_out, cm_file, &cm_data, &cm_len)) !=
        R_ERROR_NONE)
    {
        goto end;
    }

    /*
     * Load the cryptographic message buffer into a cryptographic message
     * object. Process the cryptographic message depending on the format:
     *    - Attached Data: The actual data is inside the cryptographic
     *                     message.
     *    - Detached Data: The actual data is detached from the cryptographic
     *                     message.
     * The detached data may be in a separate file or in the same file as
     * the cryptographic message and appended to the cryptographic message.
     * Furthermore the data may be wrapped (whether attached or not). All of
     * these variations need to be determined before the data can be accessed.
     */
    if ((ret = R_CM_from_binary(ctx, R_FLAG_SHARE_DATA, R_CM_TYPE_UNKNOWN,
        R_CM_ENCODING_FORMAT_WRAPPED, cm_len,
        cm_data, &num_used, &obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_CM_from_binary failure\n");
        goto end;
    }

    /*
     * Access the data to verify. Determine where the actual data is and point
     * the msg_data structure at it.
     *
     * If the data is detached but in the same file then the data follows the
     * cryptographic message. Point to the data after the cryptographic message
     * that was just read above.
     *
     * If the data is detached but in a separate file then read the data
     * from the specified file.
     *
     * If the data is attached then it is inside the cryptographic message and
     * nothing more need be done.
     */
    if (single == 1)
    {
        /*
         * Set the data message with the remainder of the data. That is,
         * detached data but in the same file as the cryptographic message.
         */
        msg_data.data = cm_data + num_used;
        msg_data.len  = cm_len  - num_used;
    }
    else if (datafile != NULL)
    {
        /*
         * If there is any message data then read that as well. That is,
         * detached data in a separate file to the cryptographic message.
         */
        if ((ret = data_from_file(bio_out, datafile, &data.data, &slen)) !=
            R_ERROR_NONE)
        {
            goto end;
        }

        /* Use the data as is */
        msg_data.data = data.data;
        msg_data.len  = data.len = slen;
    }

    /*
     * Determine the content type of the cryptographic message. The content
     * type of the cryptographic message is used to determine how to handle the
     * attached data within the cryptographic message.
     */
    if ((ret = R_CM_get_info(obj, R_CM_INFO_TYPE, &data_type)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Retrieve content type failure\n");
        goto end;
    }

    /*
     * Load the data to be verified into a cryptographic message.
     * See the individual cases below.
     */
    if ((datafile == NULL) && (single == 0))
    {

        /* Attached data */

        if (data_type != R_CM_TYPE_DATA)
        {
            /*
             * If the content is not plain data then access the
             * actual message.
             */
            if ((ret = R_CM_content_to_R_CM(obj, R_FLAG_SHARE_DATA,
                &data_obj)) != R_ERROR_NONE)
            {
                /* Empty content is allowed */
                if (ret != R_ERROR_NOT_FOUND)
                {
                    BIO_printf(bio_out, "R_CM_content_to_R_CM failure\n");
                    goto end;
                }
                else
                {
                    /* No data to print */
                    print_data = 0;
                    ret = R_ERROR_NONE;
                }
            }
        }
        else
        {
            /*
             * In this case a plain data message was handed to this application
             * so the signedData object (obj) is really the data object.
             */
            data_obj = obj;
            obj      = NULL;
        }
    }
    else if (wrap == 1)
    {
        /*
         * The detached and wrapped data case. If the detached data is wrapped,
         * strip it off.
         */
        if ((ret = R_CM_from_binary(ctx, R_FLAG_SHARE_DATA, R_CM_TYPE_UNKNOWN,
            R_CM_ENCODING_FORMAT_WRAPPED,
            msg_data.len, msg_data.data, &num_used, &data_obj)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "From binary failure\n");
            goto end;
        }
    }
    else
    {
        /*
         * As the data is detached and not wrapped, create a new cryptographic
         * message and set the data against it
         */
        if ((ret = R_CM_new(ctx, R_CM_TYPE_DATA, &data_obj)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "R_CM_new failure\n");
            goto end;
        }

        /* Set the data against the object */
        if ((ret = R_CM_set_info(data_obj, R_CM_INFO_DATA, &msg_data)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Set content data failure\n");
            goto end;
        }
    }

    /* If it is a plain data message then it can only be printed */
    if (data_type == R_CM_TYPE_DATA)
    {
        goto print_data;
    }


    /* Verify all the signers. */

    /* First see if any signers are present */
    R_CM_get_info(obj, R_CM_INFO_SIGNER_COUNT, &num_signers);

    if (num_signers > 0)
    {
        if ((ret = R_CM_signer_verify(obj, store_ctx, vfy_ctx, R_CM_INDEX_ALL,
            &is_verified)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "R_CM_signer_verify failed\n");
            goto end;
        }

        if (!is_verified)
        {
            ret = R_ERROR_FAILED;
            BIO_printf(bio_out, "Verify signer failed\n");
            goto end;
        }

        /* Print all the verification information for the signers */
        if ((ret = print_signer_info(bio_out, obj, cert_ctx,
            print_sig)) != R_ERROR_NONE)
        {
            goto end;
        }
        /*
         * Verify the data. The actual data is in data_obj and all the
         * cryptographic information required to perform the verification (for
         * example, signer info) is in obj.
         */
        if ((ret = R_CM_signature_verify(obj, R_CM_INDEX_ALL, data_obj,
            &is_verified)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "R_CM_signature_verify failed\n");
            goto end;
        }

        if (!is_verified)
        {
            ret = R_ERROR_FAILED;
            BIO_printf(bio_out, "Verify data failed\n");
            goto end;
        }

    }
    /*
     * If no signers are present then ensure that no verification has been
     * requested
     */
    else if ((options == NULL) || (Strcmp(options, "NONE") != 0))
    {
        ret = R_ERROR_FAILED;
        BIO_printf(bio_out, "Verify data failed - no signers found\n");
        goto end;
    }


    /* Print the certificates in text form */
    if (print_certs)
    {
        if((ret = print_cert_info(bio_out, obj, lib_ctx, cert_ctx))
            != R_ERROR_NONE)
        {
            goto end;
        }
    }

print_data:

    /* Print the message */
    if (print_data == 1)
    {
        if ((ret = R_CM_write(data_obj, bio_out, R_FORMAT_TEXT, NULL,
            R_CM_ENCODING_FORMAT_RAW)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Failed to write decrypted data\n");
            goto end;
        }
    }

end:

    /*
     * 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_out != NULL))
    {
        BIO_printf(bio_out, "ERROR: (%d) %s\n", ret,
            R_LIB_CTX_get_error_string(lib_ctx, R_RES_MOD_ID_LIBRARY, ret));
    }

    if (cm_data != NULL)
    {
        Free(cm_data);
    }
    if (data.data != NULL)
    {
        Free(data.data);
    }
    if (obj != NULL)
    {
        R_CM_free(obj);
    }
    if (data_obj != NULL)
    {
        R_CM_free(data_obj);
    }
    if (ctx != NULL)
    {
        R_CM_CTX_free(ctx);
    }
    if (vfy_ctx != NULL)
    {
        R_VERIFY_CTX_free(vfy_ctx);
    }
    if (store_ctx != NULL)
    {
        R_CERT_STORE_CTX_free(store_ctx);
    }
    /*
     * The certificate context cannot be freed until the store has been emptied
     * as certificates in the store hold a reference back to the certificate
     * context
     */
    if (cert_ctx != NULL)
    {
        R_CERT_CTX_free(cert_ctx);
    }
    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }
    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }

    return(R_ERROR_EXIT_CODE(ret));
}

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