RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

cm_env.c

/* $Id: cm_env.c,v 1.47 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
 * This sample demonstrates how to create a PKCS #7 enveloped data message.
 *
 * The data to be enveloped is provided in a file and specified by the -data
 * option. The data in the file may be in the form of plain data or it may be
 * wrapped. In the latter case the -wrapped switch must be used. The encrypted
 * data may be included in the PKCS #7 enveloped message or detached from the
 * message. This is controlled by the -detached switch. In a case where the
 * encrypted data is detached, it may be in a separate file or appended to the
 * PKCS #7 signed data message within the same file. If the -single switch is
 * specified, the detached data is appended in the same file.
 *
 * The data may be encrypted for a number of recipients. The recipient's
 * certificate must be specified in each case. In the case of multiple
 * recipients the information is colon separated in a continuous string (see
 * examples that follow).
 *
 * For example, to:
 *
 * Encrypt the data and include the data in a PKCS #7 enveloped data message:
 *   cm_env -out env.data -data data.txt -certs recipient.cert
 *
 * The encrypted data is detached in a separate file to the PKCS #7 message:
 *   cm_env -out env.data -data data.txt -certs recipient.cert
 *          -detached -edata data.env -print_data
 *
 * where:  env.data       = The output file where the PKCS #7 data is written.
 *         data.txt       = The data to be included in the PKCS #7 message.
 *         recipient.cert = The recipient's certificate.
 *         data.env       = The detached file containing only the encrypted
 *                          data.
 *
 */

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

/* Usage help message. */
static char *cm_env_usage[] =
{
    "usage: cm_env [options]\n",
    "where options are:\n",
    " -out file            - The file containing the cryptographic message\n",
    " -data file           - The file containing the data to envelope\n",
    " -wrapped             - The data in the file is wrapped\n",
    " -single              - The cryptographic message and the detached\n",
    "                        data are put into the same file, the\n",
    "                        cryptographic message first followed by the\n",
    "                        detached data\n",
    " -edata file          - File containing the encrypted detached data,\n",
    "                        when not appended to the cryptographic message\n",
    " -wrap                - The data when written out is to be wrapped\n",
    " -detached            - The data is detached from the message\n",
    " -enc_alg alg         - The encryption algorithm identifier to use\n",
    " -iv                  - Initialization Vector for symmetric encryption\n",
    "                        algorithm\n",
    " -key_size            - Key size in bits for symmetric encryption\n",
    "                        algorithm (RC2_CBC, RC5_CBC only)\n",
    " -eff_bits            - Effective key bits (RC2_CBC only)\n",
    " -no_rounds           - No. of rounds during encryption (RC5_CBC only)\n",
    " -certs list          - List of certificates to use (colon separated)\n",
    " -certtype encoding   - Encoding of the certificates - only X509\n",
    "                        (default) supported\n",
#ifdef NO_PEM
    " -certform format     - Format of the certificates (BIN only)\n",
#else
    " -certform format     - Format of the certificates\n"
    "                        - one of BIN (default), PEM\n",
#endif /* NO_PEM */
    " -print_recipient     - Print the certificate information for each of the"
    "\n",
    "                        recipients in the cryptographic message\n",
    " -print_data          - Print the enveloped data of the cryptographic\n",
    "                        message\n",
#ifdef NO_SOFTWARE_CRYPTO
    " -no_fips140          - Use non FIPS140 crypto implementations\n",
#endif /* NO_SOFTWARE_CRYPTO */
    " -eg                  - Example usage\n",
    NULL
};

static char *cm_env_example_usage[] =
{
    "Encrypt the data and include the data in a PKCS7 enveloped data message."
    "\n",
    "cm_env -out env.data -data data.txt -certs recipient.cert\n",
    "\n",
    "The encrypted data is detached in a separate file to the PKCS7 message\n",
    "cm_env -out env.data -data data.txt -certs recipient.cert\n",
    "       -detached -edata data.env -print_data\n",
    "\n",
    "where:  env.data       = Output file where pkcs7 data is written\n",
    "        data.txt       = The data to be included in the pkcs7 message\n",
    "        recipient.cert = Recipient's certificate\n",
    "        data.env       = Detached file containing only the encrypted data"
    "\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)
{
    int               ret       = R_ERROR_NONE;
    BIO              *bio_err   = NULL;
    BIO              *bio_out   = NULL;
    BIO              *bio_file  = NULL;
    BIO              *bio_efile = 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_CM             *edata_obj = NULL;
    R_CERT_CTX       *cert_ctx  = NULL;
    R_TITEM           enc_data  = { R_FLAG_SHARE_DATA, 0, NULL };
    R_ITEM            iv = { 0, NULL };
    char             *certfile;
    char             *cm_file;
    char             *datafile;
    char             *edatafile;
    char             *options;
    char             *str;
    R_CERT_TYPE       certtype;
    R_FORMAT          certform;
    int               detached;
    int               eff_bits;
    int               enc_alg;
    int               key_size;
    int               no_rounds;
    int               print_rec;
    int               print_data;
    int               single;
    int               wrap;
    int               wrapped;
    R_CM_ENCODING     format;

    /* Set the defaults */
    certfile     = NULL;
    cm_file      = NULL;
    options      = NULL;
    datafile     = NULL;
    edatafile    = NULL;
    certtype     = R_CERT_TYPE_X509;
    certform     = R_FORMAT_BINARY;
    enc_alg      = R_CR_ID_DES_CBC;
    detached     = 0;
    eff_bits     = 0;
    key_size     = 0;
    no_rounds    = 0;
    print_rec    = 0;
    print_data   = 0;
    single       = 0;
    wrap         = 0;
    wrapped      = 0;

    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 end;
    }

    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, "-edata") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            edatafile = *(++argv);
        }
        else if (Strcmp(*argv, "-detached") == 0)
        {
            detached = 1;
        }
        else if (Strcmp(*argv, "-single") == 0)
        {
            single = 1;
        }
        else if (Strcmp(*argv, "-wrap") == 0)
        {
            wrap = 1;
        }
        else if (Strcmp(*argv, "-wrapped") == 0)
        {
            wrapped = 1;
        }
        else if (Strcmp(*argv, "-enc_alg") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }

            str = *(++argv);

            if ((ret = R_CR_ID_from_string(str, &enc_alg)) != R_ERROR_NONE)
            {
                BIO_printf(bio_err, "Bad algorithm identifier: %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-key_size") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            key_size = atoi(*(++argv));
        }
        else if (Strcmp(*argv, "-eff_bits") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            eff_bits = atoi(*(++argv));
        }
        else if (Strcmp(*argv, "-no_rounds") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            no_rounds = atoi(*(++argv));
        }
        else if (Strcmp(*argv, "-iv") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }

            iv.data = (unsigned char *)*(++argv);
            iv.len = (unsigned int)Strlen((char *)iv.data);
        }
        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_err, "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_err, "Unknown certificate format %s\n", str);
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-print_recipient") == 0)
        {
            print_rec = 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_env_example_usage; (*egp) != NULL; egp++)
            {
                BIO_printf(bio_out, *egp);
            }
            goto end;
        }
        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_env_usage; (*pp != NULL); pp++)
        {
            BIO_printf(bio_err, *pp);
        }
        goto end;
    }

    /*
     * Create a library context to provide access to all configurable aspects
     * of the library
     */

    /* Create a new library context */
    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 end;
    }

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

    /* 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_err, "Certificate context new failure\n");
        goto end;
    }

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

    /* Load the data to be enveloped into a cryptographic message context */
    format = R_CM_ENCODING_FORMAT_RAW;
    if (wrapped == 1)
    {
        format = R_CM_ENCODING_FORMAT_WRAPPED;
    }

    if ((ret = R_CM_read_file(ctx, datafile, R_FORMAT_BINARY, format,
        &data_obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to read data from file %s\n", datafile);
        goto end;
    }

    /*
     * Configure the enveloped data object. The enveloped data message will
     * eventually hold all of the information for the cryptographic message.
     * Set up all the information that will be required to construct the
     * cryptographic message, such as the:
     *     - Recipient certificates.
     *     - Encryption details, such as key size and Initialization
     *       Vector (IV).
     *     - Data to be enveloped.
     */

    /* Create a new enveloped data message */
    if ((ret = R_CM_new(ctx, R_CM_TYPE_ENVELOPED_DATA, &obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "R_CM_new failure (Enveloped Data)\n");
        goto end;
    }

    /* Add all recipients to the enveloped data message */
    if ((ret = add_recipients(bio_err, cert_ctx, obj, certfile, certtype,
        certform)) != R_ERROR_NONE)
    {
        goto end;
    }

    /* Set the data against the object */
    if ((ret = R_CM_content_from_R_CM(obj, R_FLAG_SHARE_DATA, data_obj)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Setting the content failed\n");
        goto end;
    }

    /* Set the key size against the object (optional) */
    if (key_size != 0)
    {
        if ((ret = R_CM_set_info(obj, R_CM_INFO_KEY_SIZE, &key_size)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Set symmetric key size failure\n");
            goto end;
        }
    }

    /* Set the effective bits of the key against the object (optional) */
    if (eff_bits != 0)
    {
        if ((ret = R_CM_set_info(obj, R_CM_INFO_NO_BITS, &eff_bits)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Set symmetric key size failure\n");
            goto end;
        }
    }

    /* Set the number of encryption rounds against the object (optional) */
    if (no_rounds != 0)
    {
        if ((ret = R_CM_set_info(obj, R_CM_INFO_NO_ROUNDS, &no_rounds)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Set symmetric key size failure\n");
            goto end;
        }
    }

    /* Set the IV against the object (optional) */
    if (iv.data != NULL)
    {
        if ((ret = R_CM_set_info(obj, R_CM_INFO_IV, &iv)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Set initialization vector failure\n");
            goto end;
        }
    }

    /* Envelope the data. All recipients are enveloped. */

    /* Let the cryptographic message object generate its own key */
    if ((ret = R_CM_encrypt(obj, enc_alg, R_CM_INDEX_ALL)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to encrypt\n");
        goto end;
    }

    /* Print the data */

    /* Display the recipient details if requested */
    if (print_rec == 1)
    {
        /*
         * Print the enveloped data message. If print_data != 0 the
         * encrypted data will also be printed to bio_out.
         */
        if ((ret = R_CM_write(obj, bio_err, R_FORMAT_TEXT, &print_data, 0)) !=
            R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Write envelopedData data failure\n");
            goto end;
        }
    }

    /* Output the cryptographic message */
    if (cm_file != NULL)
    {
        /* Select the correct formatting for the enveloped data message */
        format = R_CM_ENCODING_FORMAT_WRAPPED;

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

        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 end;
        }

        /* Write out the enveloped 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 envelopedData to file\n");
            goto end;
        }

        if (detached != 0)
        {
            /* Retrieve the encrypted data from the R_CM */
            if ((ret = R_CM_get_info(obj, R_CM_INFO_ENC_DATA, &enc_data)) ==
                R_ERROR_NONE)
            {
                if ((ret = R_CM_from_binary(ctx, R_FLAG_SHARE_DATA,
                    R_CM_TYPE_DATA, R_CM_ENCODING_FORMAT_RAW, enc_data.len,
                    enc_data.data, NULL, &edata_obj)) != R_ERROR_NONE)
                {
                    BIO_printf(bio_err, "R_CM_from_binary failure\n");
                    goto end;
                }
            }

            /* Write out encrypted data in the format required */
            if (wrap != 0)
            {
                format = R_CM_ENCODING_FORMAT_WRAPPED;
            }
            else
            {
                format = R_CM_ENCODING_FORMAT_RAW;
            }

            /*
             * Append the encrypted data to the same file as the cryptographic
             * message
             */
            if (single != 0)
            {
                if ((ret = R_CM_write(edata_obj, bio_file, R_FORMAT_BINARY,
                    NULL, format)) != R_ERROR_NONE)
                {
                    BIO_printf(bio_err, "Failed to write the appended data\n");
                }
            }
            /* Write the encrypted data into a separate file */
            else if (edatafile != NULL)
            {
                if ((bio_efile = BIO_new_file(edatafile, "wb")) == NULL)
                {
                    BIO_printf(bio_err, "Failed to open file: %s\n", edatafile);
                    ret = R_ERROR_ALLOC_FAILURE;
                    goto end;
                }

                if ((ret = R_CM_write(edata_obj, bio_efile, R_FORMAT_BINARY,
                    NULL, format)) != R_ERROR_NONE)
                {
                    BIO_printf(bio_err, "Failed to write the detached 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_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_obj != NULL)
    {
        R_CM_free(data_obj);
    }
    if (edata_obj != NULL)
    {
        R_CM_free(edata_obj);
    }
    if (ctx != NULL)
    {
        R_CM_CTX_free(ctx);
    }
    if (cert_ctx != NULL)
    {
        R_CERT_CTX_free(cert_ctx);
    }
    if (bio_file != NULL)
    {
        BIO_free(bio_file);
    }
    if (bio_efile != NULL)
    {
        BIO_free(bio_efile);
    }
    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }
    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }
    if (bio_err != NULL)
    {
        BIO_free(bio_err);
    }

    return(R_ERROR_EXIT_CODE(ret));
}


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