RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

cm_env_sm.c

/* $Id: cm_env_sm.c,v 1.22 2005/02/08 22:55:30 jmckee 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_env_sm.c
 * This sample demonstrates PKCS #7 enveloped data operations, and contains
 * the following steps:
 *
 *    - Creates a PKCS #7 enveloped data message.
 *    - Writes the enveloped message to a file.
 *    - Reads the enveloped data message back from file into an R_CM structure.
 *    - Decrypts the data message.
 *    - Prints various data fields from the enveloped data message.
 *    - Finally prints the actual data.
 *
 * This is a self-contained program and therefore requires no command line
 * parameters. The data to envelope is defined as a text string.
 */

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

/*
 * 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;
    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_CERT           *cert      = NULL;
    R_PKEY_CTX       *pkey_ctx   = NULL;
    R_PKEY           *pkey       = NULL;
    char             *cm_file;
    char              cdata[]="Data to envelope - 0987654321";
    int               enc_alg;
    unsigned int      consumed_len;
    int               index;
    R_CM_ENCODING     format;
    unsigned char     buf[128];
    int var;

    /* Set the defaults */
    enc_alg      = R_CR_ID_DES_CBC;
    cm_file      = "cm_env_sm.out";

    /* Create an output channel */
    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);

    /*
     * Retrieve the default resource list and create a library context to
     * provide access to all configurable aspects of the library
     */

    /* Retrieve the default resource list */
    res_list = PRODUCT_DEFAULT_RESOURCE_LIST();

    /* 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, public key and cryptographic message contexts.
     * These contexts are required if any R_CERT_CTX *, R_PKEY_CTX * and
     * R_CM_CTX * routines are used.
     */

    /* Create a new certificate context */
    if ((ret = R_CERT_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_CERT_TYPE_X509,
        &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;
    }

    /* Create a new private key context */
    if ((ret = R_PKEY_CTX_new(lib_ctx, R_RES_FLAG_DEF, R_PKEY_TYPE_RSA,
        &pkey_ctx)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Private key context new failure\n");
        goto end;
    }

    /*
     * Create a certificate object. Read the binary representation of the
     * certificate and convert it to an R_CERT object. If the object does not
     * exist (for example, cert == NULL) then memory for an R_CERT object is
     * allocated. The public key of the certificate is used to encrypt the
     * enveloped data for a particular recipient.
     */
    if ((ret = R_CERT_from_binary(cert_ctx, R_FLAG_SHARE_NONE, R_CERT_TYPE_X509,
        sizeof(signer_certificate), signer_certificate, &consumed_len,
        &cert)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_CERT_from_binary failure for CA certificate\n");
        goto end;
    }

    /*
     * Read the message data into a cryptographic message object. The data is
     * then in a convenient form to be included into the final cryptographic
     * message structure in later steps, prior to encrypting the message.
     */
    BIO_printf(bio_out, "The Original Message Data is: %s\n", cdata);

    /* Parse the data from the string into the R_CM object */
    if ((ret = R_CM_from_binary(ctx, R_FLAG_SHARE_NONE, R_CM_TYPE_DATA,
        R_CM_ENCODING_FORMAT_RAW, sizeof(cdata), (unsigned char *)cdata,
        &consumed_len, &data_obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to read message data\n");
        goto end;
    }

    /*
     * Create and configure the enveloped data object. The sub-steps involved
     * are as follows:
     *    - Create a new enveloped data message object.
     *    - Add recipient information.
     *    - Include the actual data (created in the previous step).
     */

    /* 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 the recipient information */
    if ((ret = R_CM_recipient_add(obj, cert, NULL)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to add recipient\n");
        goto end;
    }

    /*
     * This certificate is required to open the enveloped message, so the
     * reference count must be incremented. The R_CM is responsible for
     * freeing any certificates used to create the cryptographic message.
     */
    R_CERT_inc_reference(cert);

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

    /*
     * Encrypt the data in the enveloped data message for all recipients
     */

    /* Let the cryptographic message object generate its own symmetric 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;
    }

    /* Output the message */

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

    /* Select the correct formatting for the enveloped data message */
    format = R_CM_ENCODING_FORMAT_WRAPPED;

    /* Write out the enveloped data message and free the objects to reuse */
    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;
    }

    BIO_printf(bio_out, "Encrypted data successfully written to file\n");

    /* The next two R_CM objects will be created later */
    if (obj != NULL)
    {
        R_CM_free(obj);
        obj = NULL;
    }

    if (data_obj != NULL)
    {
        R_CM_free(data_obj);
        data_obj = NULL;
    }

    /* Close the BIO as the next read requires the file to close */
    if( bio_file != NULL )
    {
        BIO_free( bio_file );
        bio_file = NULL;
    }

    /* Read the enveloped data message from file */

    /* Select the correct formatting for the enveloped data message */
    format = R_CM_ENCODING_FORMAT_WRAPPED;

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

    BIO_printf(bio_out, "Enveloped data successfully read back from file\n");

    /*
     * Create the key object. Read the binary representation of the private key
     * and convert it into an R_PKEY object. The private key is used to decrypt
     * the enveloped data for a particular recipient.
     */

    if ((ret = R_PKEY_from_binary( pkey_ctx, R_PKEY_FL_DEFAULT, R_PKEY_TYPE_RSA,
        sizeof(signer_priv_key_data), (unsigned char *)signer_priv_key_data,
        &consumed_len, &pkey)) != R_ERROR_NONE)
    {
        BIO_printf(bio_out, "R_PKEY_from_binary failure - for private key\n");
        goto end;
    }

    /*
     * Decrypt the enveloped data message. Retrieve the index of the recipient
     * to decrypt based on the certificate matching the private key. Note the
     * decryption requires that the symmetric key is decrypted with the
     * private key of the recipient. Then the symmetric key is used to
     * decrypt the encrypted message. The private key of the recipient is read
     * from memory below. This key is passed to R_CM_decrypt(). The certificate
     * (whose public key signed the symmetric key during the creation of the
     * enveloped data message) is used to look up the list of recipient_infos
     * to find the index of this particular recipient (in this simple sample
     * there is only one recipient). This index is passed to R_CM_decrypt()
     * with the corresponding private key in order to decrypt the correct
     * symmetric key, which in turn is used to decrypt the encrypted message
     * data.
     */

     /*
      * Retrieve the index of this recipient. Note there is only one in this
      * sample.
      */
    if ((ret = R_CM_get_index(obj, cert, &index)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to look up the recipient certificate\n");
        goto end;
    }

    /* Decrypt the data */
    if ((ret = R_CM_decrypt(obj, pkey, index)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Failed to decrypt with key\n");
        goto end;
    }

    BIO_printf(bio_out, "Data successfully decrypted with the key\n");

    /* Print out the data and a portion of the signed data message fields */
    BIO_printf(bio_out, "Print some of the data fields!\n");

    /* The message type. In this sample, it is enveloped data */
    if (R_CM_get_info(obj, R_CM_INFO_TYPE, &var) == R_ERROR_NONE)
    {
        R_CM_TYPE_to_string(var, sizeof(buf), (char *)buf);
        BIO_printf(bio_out, "Message type: %d (%s)\n", var, buf);
    }

    if (R_CM_get_info(obj, R_CM_INFO_VERSION, &var) == R_ERROR_NONE)
    {
        BIO_printf(bio_out, "Message version: %d\n", var);
    }

    /* The number of recipients. In this sample, there is one. */
    var = 0;
    R_CM_get_info(obj, R_CM_INFO_RECIPIENT_COUNT, &var);
    BIO_printf(bio_out, "Recipient count: %d\n", var);

    /* The encryption algorithm */
    if (R_CM_get_info(obj, R_CM_INFO_ENCRYPTION_ALG, &var) == R_ERROR_NONE)
    {
        if (R_CR_ID_to_string(var, sizeof(buf), (char *)buf) == R_ERROR_NONE)
        {
            BIO_printf(bio_out, "Content encryption algorithm: %d (%s)\n", var,
                buf);
        }
    }

    /* Retrieve the content, returning the decrypted message data */
    if ((ret = R_CM_content_to_R_CM(obj, R_FLAG_SHARE_DATA, &data_obj)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Retrieve content data failure\n");
        goto end;
    }

    BIO_printf(bio_out, "The decrypted data is as follows:\n");

    if ((ret = R_CM_write(data_obj, bio_out, R_FORMAT_TEXT, NULL,
        R_CM_ENCODING_FORMAT_RAW)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "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_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 (ctx != NULL)
    {
        R_CM_CTX_free(ctx);
    }
    if (pkey != NULL)
    {
        R_PKEY_free(pkey);
    }
    if (pkey_ctx != NULL)
    {
        R_PKEY_CTX_free(pkey_ctx);
    }
    /* Free the certificate, as the reference was incremented earlier */
    if (cert != NULL)
    {
        R_CERT_free(cert);
    }
    if (cert_ctx != NULL)
    {
        R_CERT_CTX_free(cert_ctx);
    }
    if (bio_file != NULL)
    {
        BIO_free(bio_file);
    }
    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