RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

cm_smpl.c

/* $Id: cm_smpl.c,v 1.45 2005/08/08 05:33:31 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
 * This sample demonstrates verifying and printing PKCS #7 signed data
 * messages. No certificate verification is performed.
 *
 * For information detailing generating PKCS #7 signed messages,
 * see cm_sign.c.
 *
 * For example, to:
 *
 * Verify the signature and print the data:
 *   cm_smpl -cm_msg signed.data -print_data
 *
 * Verify the signature with the certificate not in the PKCS #7 message
 * (the signer's certificate is supplied):
 *   cm_smpl -cm_msg signed.data -certs signer.cert -print_signers -print_data
 *
 * where: signed.data = The output file where PKCS #7 data is written.
 *        signer.cert = The signer's certificate (used when the signed data
 *                      was created).
 */

/* This sample only parses messages and does not print the signer(s) details */
#define NO_R_CM_ADD_SIGNERS
#define NO_R_CM_PRINT_SIGNER

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

#ifdef CM_SMPL_SMALL

/* For a small executable use a minimized resource list */
#include "r_cm_r.h"
#define CM_SMPL_RESOURCE_LIST R_CM_get_verify_small_resource_list
#define CM_SMPL_CTX_TYPE      R_CM_TYPE_SIGNED_DATA_READ

#else /* !CM_SMPL_SMALL */

#define CM_SMPL_RESOURCE_LIST PRODUCT_DEFAULT_RESOURCE_LIST
#define CM_SMPL_CTX_TYPE      R_CM_TYPE_DEFAULT

#endif /* !CM_SMPL_SMALL */

/* Usage help message */
static char *cm_smpl_usage[] =
{
    "usage: cm_smpl [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 of"
    "\n",
    "                         the cryptographic message\n",
    " -single               - The cryptographic message and detached data are"
    "\n",
    "                         in the same file, the cryptographic message\n",
    "                         first followed by the detached data\n",
    " -wrap                 - The detached message is wrapped and should be\n",
    "                         and should be unwrapped before use\n",
    " -certs list           - The list of certificates (colon separated)\n",
    " -certtype encoding    - The encoding of the certificates - only X509\n",
    "                         (default) supported\n",
#ifdef NO_PEM
    " -certform format      - Format of the certs (BIN only)\n",
#else
    " -certform format      - Format of the certs one of BIN (default), PEM\n",
#endif /* NO_PEM */
    " -print                - Print the data of the cryptographic message\n",
#ifdef NO_SOFTWARE_CRYPTO
    " -no_fips140           - Use non FIPS140 crypto implementations\n",
#endif /* NO_SOFTWARE_CRYPTO */
    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;
    unsigned char    *cm_data   = NULL;
    unsigned char    type[R_CM_TYPE_MAX_STR_LENGTH];
    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;
    unsigned int      slen;

    /* Set the defaults */
    cm_file   = NULL;
    datafile  = NULL;
    certfile  = NULL;
    certtype  = "X509";
    certform  = "BIN";
    single    = 0;
    wrap      = 0;
    print     = 0;

    res_list = CM_SMPL_RESOURCE_LIST();

    /*
     * Create 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;
    }

    /* Parse the command line parameters */

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

    /* Process all command line options */
    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, "-print") == 0)
        {
            print = 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
        {
            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_smpl_usage; (*pp != NULL); pp++)
        {
            BIO_printf(bio_out, *pp);
        }
        goto end;
    }

    /*
     * 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_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. This
     * sample does not perform any certificate verification. For certificate
     * verification, see cm.c or cm_adv.c.
     */
    if ((ret = set_verification(bio_out, lib_ctx, "NONE", NULL, &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, CM_SMPL_CTX_TYPE,
        &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 data is inside the cryptographic
     *                     message.
     *    - Detached Data: The 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). 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, "From binary failure\n");
        goto end;
    }

    /*
     * Access the data to verify. Determine where the data is and point the
     * msg_data structure at it.
     *
     * If the data is detached but in the same file, 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, read the data from the
     * specified file.
     *
     * If the data is attached, it is inside the cryptographic message and no
     * further action must be taken.
     */
    if (single == 1)
    {
        /* Set the data message with the remainder of the data */
        msg_data.data = cm_data + num_used;
        msg_data.len  = cm_len  - num_used;
    }
    /* If there is any message data then read that as well */
    else if (datafile != NULL)
    {
        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. Check to see if the data
     * is signed data. If it is, verify it. Otherwise, print the message only.
     */
    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;
    }
    else
    {
        BIO_printf(bio_out, "Content Type:%d\n",data_type);

        if ((ret = R_CM_TYPE_to_string(data_type, sizeof(type),
            (char *)type)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Content type string conversion failure\n");
        }
        else
        {
            BIO_printf(bio_out, "Content Type: %s\n",type);
        }
    }

    /*
     * Load the data to be verified into a cryptographic message. See the
     * individual cases below.
     */

    /* If the data is attached, obtain it from the object */
    if ((datafile == NULL) && (single == 0))
    {
        /* Attached data */
        if (data_type != R_CM_TYPE_DATA)
        {
            /* If the content is not plain data access the actual message */
            if ((ret = R_CM_content_to_R_CM(obj, R_FLAG_SHARE_DATA,
                &data_obj)) != R_ERROR_NONE)
            {
                BIO_printf(bio_out, "R_CM_content_to_R_CM failure\n");
                goto end;
            }
        }
        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)
    {
        /*
         * 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, "R_CM_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;
        }
    }

    /*
     * Verify the cryptographic message. The actual data is in data_obj and all
     * the cryptographic information required to perform the verification (for
     * example, signer info) is in obj.
     */

    /* Check for signed data */
    if (data_type == R_CM_TYPE_SIGNED_DATA)
    {
        /*
         * Verify the signer data. No certificate verification is performed so
         * pass NULL for the store context and vfy_ctx. See cm.c and cm_adv.c
         * for certificate verification.
         */
        if ((ret = R_CM_verify(obj, store_ctx, vfy_ctx, R_CM_INDEX_ALL,
            data_obj, &is_verified)) != R_ERROR_NONE)
        {
            BIO_printf(bio_out, "R_CM_verify failed\n");
            goto end;
        }

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

    /* Print the message */
    if (print == 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");
        }
    }

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 the 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