| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: cm_adv.c,v 1.43 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 advanced PKCS #7 signed data operations using two * user-supplied application-specific callback functions, which are added to * the resource list. * * In the time verification function, the certificate is extracted from the * verification details object. The notBefore and notAfter times are extracted * from the certificate and printed. No actual action is taken on the time * information in this sample. * * In the certificate details verification function the certificate is * extracted from the verification details object. The extension information * is extracted from the certificate and printed. No actual action is taken * on the extension information in this sample. */ #include "r_prod.h" #include "cm_com.h" #define MAX_BUF_LEN 256 #define STRING_SEPERATOR " " static int set_resources(BIO *bio_out, R_RES_LIST **res_list, int set); static R_VERIFY_TIME_FUNC_T *get_vfy_time_func(void *imp_data); static R_VERIFY_DETAIL_FUNC_T *get_vfy_detail_func(void *imp_data); static int vfy_time_func(R_VERIFY_STATE *state, R_VERIFY_DETAILS *detail); static int vfy_detail_func(R_VERIFY_STATE *state, R_VERIFY_DETAILS *detail, int index); /* Usage help message. */ static char *cm_adv_usage[] = { "usage: cm_adv [options]\n", "where options are:\n", " -cm_msg file - The file containing the cryptographic message\n", " -data_msg file - 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 " "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 - List of certificates (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 " -vfy_opts option - Certificate verification options - one of" "NONE, DEF,\n", " ALL, NO_COMPLETE\n", " -set_vfy - Sets the additional verification functions\n", " for verification of the name and times in a\n" " certificate\n", " -no_signer - Do not verify the signers\n", " -no_data - Do not verify the data\n", " -print_signer - Print the certificate information for each of" "the\n", " signers in the cryptographic message\n", " -print_data - Print the signed data of the cryptographic" "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 */ 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.<br> * See @ref R_ERROR_IDS for valid values. */ int main(int argc, char **argv) { 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_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 = NULL; 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 = 0; int single = 0; int print_sig; int print_data; int no_signer = 0; int no_data = 0; int vfy_opts; unsigned int slen; char *vfy_time = NULL; /* Set the defaults */ cm_file = NULL; datafile = NULL; certfile = NULL; certtype = "X509"; certform = "BIN"; options = NULL; vfy_opts = 0; print_sig = 0; print_data = 0; single = 0; wrap = 0; no_signer = 0; no_data = 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; } /* 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, "-vfy_opts") == 0) { if (--argc < 1) { goto bad; } options = *(++argv); } else if (Strcmp(*argv, "-set_vfy") == 0) { vfy_opts = 1; } else if (Strcmp(*argv, "-no_signer") == 0) { no_signer = 1; } else if (Strcmp(*argv, "-no_data") == 0) { no_data = 1; } else if (Strcmp(*argv, "-print_signer") == 0) { print_sig = 1; } else if (Strcmp(*argv, "-print_data") == 0) { print_data = 1; } 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_adv_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. The default list is updated with extra functions to * perform extended verification. */ /* Update the resource list if required */ if ((ret = set_resources(bio_out, &res_list, vfy_opts)) != R_ERROR_NONE) { goto end; } /* Create a new library context */ 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 sets the verification options */ if ((ret = set_verification(bio_out, lib_ctx, options, vfy_time, &vfy_ctx)) != R_ERROR_NONE) { 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_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). * 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, "From binary failure on cryptographic message\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, 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 needs to be taken. */ if (single == 1) { /* * Set the data message with the remainder of the data (if the data is * detached 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 read that as well (if the data is * detached but 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. This indicates * 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, 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 the 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 on the wrapped data\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 the data is plain data, it can only be printed */ if (data_type == R_CM_TYPE_DATA) { goto print_data; } /* * Verify the signer unless it is not required. If the signers are not * verified, the key that will be used to verify the data is not * authenticated, making the verification process less secure. */ if (no_signer != 1) { /* Verify all the signers */ if ((ret = R_CM_signer_verify(obj, store_ctx, vfy_ctx, R_CM_INDEX_ALL, &is_verified)) != R_ERROR_NONE) { BIO_printf(bio_out, "\nR_CM_signer_verify failed\n"); goto end; } if (!is_verified) { ret = R_ERROR_FAILED; BIO_printf(bio_out, "\nVerify 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. Verify the data unless it is not * required. If the data is not verified it is not proved that a signer * actually signed the data, making the verification process less secure. */ if (no_data != 1) { /* Finally verify the data */ 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; } } 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 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)); } /* * Sets the resource list with extra verification functions. * * @param bio_out [Out] The BIO for error messages. * @param res_list [Out] The resource list. * @param set [In] The flag to indicate if action it to be taken. * Non-zero indicates action is required. * * @returns R_ERROR_NONE indicates success. * See @ref R_ERROR_IDS for valid values. */ static int set_resources(BIO *bio_out, R_RES_LIST **res_list, int set) { int ret = R_ERROR_NONE; /* If nothing needs to be set then simply return */ if (set) { /* Set the certificate's time verification callback */ if ((ret = R_VERIFY_set_time_verify_func(res_list, get_vfy_time_func)) != R_ERROR_NONE) { BIO_printf(bio_out, "Set time callback failure\n"); } /* Set the details verification callback */ else if ((ret = R_VERIFY_set_detail_verify_func(res_list, get_vfy_detail_func)) != R_ERROR_NONE) { BIO_printf(bio_out, "Set details callback failure\n"); } } return(ret); } /* * Details the time verification get function that is inserted into the * resource list. * * @param imp_data [In] Not used. * * @returns A pointer to the details time verification function. */ static R_VERIFY_TIME_FUNC_T *get_vfy_time_func(void *imp_data) { return(&vfy_time_func); } /* * Details the verification get function that is inserted into the resource * list. * * @param imp_data [In] Not used. * * @returns A pointer to the details verification function. */ static R_VERIFY_DETAIL_FUNC_T *get_vfy_detail_func(void *imp_data) { return(&vfy_detail_func); } /* * Application-specific details time verification function that will be set in * the resource list. * * @param state [In] The verification state object. * @param detail [In] The verification details object. * * @returns R_ERROR_NONE indicates success. * See @ref R_ERROR_IDS for valid values. * * @note The certificate is extracted from the verify details object. The * notBefore and notAfter times are extracted from the certificate * and printed. No actual action is taken on the time information * in this sample. */ static int vfy_time_func(R_VERIFY_STATE *state, R_VERIFY_DETAILS *detail) { int ret; R_VERIFY_CTX *vfy_ctx; R_CERT *cert; BIO *bio_out; unsigned char *data; unsigned int length; /* Access the verification context */ if ((ret = R_VERIFY_STATE_get_R_VERIFY_CTX(state, &vfy_ctx)) != R_ERROR_NONE) { goto done; } /* Extract the BIO for printing */ if ((ret = R_VERIFY_CTX_get_info(vfy_ctx, R_VERIFY_CTX_INFO_CALLBACK_ARG, &bio_out)) != R_ERROR_NONE) { goto done; } /* Print something to show that verification time has been reached */ BIO_printf(bio_out, "\n\t\t----- REACHED VFY_TIME_FUNC ----\n\n"); /* Extract the certificate from the details */ if ((ret = R_VERIFY_DETAILS_cert_to_R_CERT(detail, R_VERIFY_DETAIL_FLAG_BY_REFERENCE, &cert)) != R_ERROR_NONE) { goto done; } /* Retrieve the notBefore field and print its data */ R_CERT_not_before_to_binary(cert, 0, NULL, &length); data = (unsigned char *)Malloc(length); R_CERT_not_before_to_binary(cert, length, data, &length); BIO_printf(bio_out, "NOT BEFORE DATA:\n"); if ((data != NULL) && (length != 0)) { BIO_dump(bio_out, data, length); } else { BIO_printf(bio_out, "\tNot available\n\n"); } if (data != NULL) { Free(data); } /* Retrieve the notAfter field and print its data */ R_CERT_not_after_to_binary(cert, 0, NULL, &length); data = (unsigned char *)Malloc(length); R_CERT_not_after_to_binary(cert, length, data, &length); BIO_printf(bio_out, "\nNOT AFTER DATA:\n"); if ((data != NULL) && (length != 0)) { BIO_dump(bio_out, data, length); } else { BIO_printf(bio_out, "\tNot available\n\n"); } if (data != NULL) { Free(data); } done: return(ret); } /* * An application-specific details verification function that will be set in * the resource list. * * @param state [In] The verification state object. * @param detail [In] The verification details object. * @param index [In] The index of the detail object in the state object. * Not used in this sample. * * @returns R_ERROR_NONE indicates success. * See @ref R_ERROR_IDS for valid values. * * @note The certificate is extracted from the verification details object. * The extension information is extracted from the certificate and * printed. No actual action is taken on the extension information * in this sample. */ static int vfy_detail_func(R_VERIFY_STATE *state, R_VERIFY_DETAILS *detail, int index) { int ret; R_VERIFY_CTX *vfy_ctx; R_CERT *cert; BIO *bio_out; char ku_str[30]; char *str=NULL; int count=0; Memset(ku_str, 0, 30); /* Access the verification context */ if ((ret = R_VERIFY_STATE_get_R_VERIFY_CTX(state, &vfy_ctx)) != R_ERROR_NONE) { goto done; } /* Extract the BIO for printing */ if ((ret = R_VERIFY_CTX_get_info(vfy_ctx, R_VERIFY_CTX_INFO_CALLBACK_ARG, &bio_out)) != R_ERROR_NONE) { goto done; } /* Print something to show that verification time has been reached */ BIO_printf(bio_out, "\n\t\t----- REACHED VFY_DETAIL_FUNC ----\n\n"); /* Extract the certificate from the details */ if ((ret = R_VERIFY_DETAILS_cert_to_R_CERT(detail, R_VERIFY_DETAIL_FLAG_BY_REFERENCE, &cert)) != R_ERROR_NONE) { goto done; } /* Print the header */ BIO_printf(bio_out, "EXTENSIONS:"); if (R_CERT_is_info_present(cert, R_CERT_INFO_EXTENDED_KEY_USAGE)) { count++; BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "\tExtended Key Usage\n"); if (R_CERT_is_info_critical(cert, R_CERT_INFO_EXTENDED_KEY_USAGE)) { BIO_printf(bio_out, "\tExtension is critical\n"); } else { BIO_printf(bio_out, "\tExtension is not critical\n"); } /* Print more information based on the extension */ if ((ret = R_CERT_extended_key_usage_to_string(cert, STRING_SEPERATOR, sizeof(ku_str), ku_str)) != R_ERROR_NONE) { BIO_printf(bio_out, "\tUnknown Extended Key Usage Data\n"); } else { BIO_printf(bio_out, "\t%s Data\n", ku_str); } } if (R_CERT_is_info_present(cert, R_CERT_INFO_KEY_USAGE)) { count++; BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "Key Usage\n"); if (R_CERT_is_info_critical(cert, R_CERT_INFO_KEY_USAGE)) { BIO_printf(bio_out, "\tExtension is critical\n"); } else { BIO_printf(bio_out, "\tExtension is not critical\n"); } if ((str = (char *)Malloc(MAX_BUF_LEN)) == NULL) { goto done; } Memset(str, 0, MAX_BUF_LEN); if ((ret = R_CERT_key_usage_to_string(cert, STRING_SEPERATOR, MAX_BUF_LEN, str)) != R_ERROR_NONE) { goto done; } if (str[0] == 0) { BIO_printf(bio_out, "\tNo Key Usage Set\n"); } else { BIO_printf(bio_out, "\t%s\n", str); } } if (count == 0) { BIO_printf(bio_out, " None\n"); } done: if (str != NULL) { Free(str); } return(ret); }