RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

r_sign.c

/* $Id: r_sign.c,v 1.37 2005/02/08 05:57:17 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 r_sign.c
 * This sample demonstrates signing and verification operations.
 *
 * For example, to:
 *
 * Sign and verify a string using SHA1 with RSA encryption:
 * r_sign -alg RSA_SHA1 -priv_file priv.key -pub_file pub.key
 * -string "hello world"
 *
 * where: priv.key = RSA private key stored in binary format
 *        pub.key = RSA public key stored in binary format
 */
#include "r_prod.h"
#include "cryp_mod.h"

/* The maximum signature buffer size for this sample */
#define MAX_SIGN_SIZE 1024

/* Usage message */
static char *r_sign_usage[] =
{
    "usage: r_sign [options]\n",
    "where options are:\n",
    " -alg value    - Signing algorithm, one of RSA_SHA1 (default),\n",
    "                 RSA_MD5, RSA_X931_SHA1, DSA_SHA1 or DSA\n",
    " -priv_file    - Private key to sign the data with, in RSA\n",
    "                 binary format\n",
    " -pub_file     - Public key to verify the data with, in RSA\n",
    "                 binary format\n",
    " -string value - String to sign and verify\n",
#ifdef NO_SOFTWARE_CRYPTO
    " -no_fips140     - Use non FIPS140 operating mode\n",
    " -fips140_ssl    - Use FIPS140 SSL operating mode\n",
#endif /* NO_SOFTWARE_CRYPTO */
    " -help         - Print this help menu\n",
    NULL
};

static int sign_data(BIO *bio_err, R_CR_CTX *ctx, R_PKEY_CTX *pkey_ctx,
    R_PKEY_TYPE pkey_type, int alg, char *privfile, char *string,
    unsigned char *obuf, unsigned int *olen);
static int verify_data(BIO *bio_err, R_CR_CTX *ctx, R_PKEY_CTX *pkey_ctx,
    R_PKEY_TYPE pkey_type, int alg, char *pubfile, char *string,
    unsigned char *dbuf, unsigned int dlen, int *result);
static int data_from_file(BIO *bio_err, char *filename, unsigned char **dbuf,
    unsigned int *dlen);

/*
 * 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)
{
    int             ret         = R_ERROR_NONE;
    int             result      = 1;
    BIO             *bio_out    = NULL;
    BIO             *bio_err    = NULL;
    R_RES_LIST      *res_list;
    R_LIB_CTX       *lib_ctx    = NULL;
    R_CR_CTX        *ctx        = NULL;
    R_CR_ALG_ID     alg         = R_CR_ID_SHA1_RSA;
    R_PKEY_CTX      *pkey_ctx   = NULL;
    R_PKEY_TYPE     pkey_type   = R_PKEY_TYPE_RSA;
    char            *string     = NULL;
    char            *str;
    char            *pubfile    = NULL;
    char            *privfile   = NULL;
    unsigned char   buf[MAX_SIGN_SIZE];
    unsigned int    len         = MAX_SIGN_SIZE;
#ifdef NO_SOFTWARE_CRYPTO
    R_FIPS140_OPERATING_MODE_T  operating_mode = FIPS140_MODE;
#endif /* NO_SOFTWARE_CRYPTO */

    /* Set the default values */
    res_list = PRODUCT_DEFAULT_RESOURCE_LIST();

    /*
     * Create BIOs to stdout and stderr. BIOs are the Basic Input/Output
     * mechanism provided by RSA and are recommended for all input and output
     * from applications.
     */
    bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
    bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);

    if ((bio_out == NULL) || (bio_err == 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, "-alg") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }

            str = *(++argv);

            if (Strcmp(str, "RSA_SHA1") == 0)
            {
                alg = R_CR_ID_SHA1_RSA;
            }
            else if (Strcmp(str, "RSA_MD5") == 0)
            {
                alg = R_CR_ID_MD5_RSA;
            }
            else if (Strcmp(str, "RSA_X931_SHA1") == 0)
            {
                alg = R_CR_ID_SHA1_RSA_X931;
            }
            else if (Strcmp(str, "DSA_SHA1") == 0)
            {
                alg = R_CR_ID_SHA1_DSA;
                pkey_type = R_PKEY_TYPE_DSA;
            }
            else if (Strcmp(str, "DSA") == 0)
            {
                alg = R_CR_ID_DSA;
                pkey_type = R_PKEY_TYPE_DSA;
            }
            else
            {
                BIO_printf(bio_err, "Unknown algorithm\n");
                ret = R_ERROR_BAD_PARAMETER;
                goto bad;
            }
        }
        else if (Strcmp(*argv, "-priv_file") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            privfile = *(++argv);
        }
        else if (Strcmp(*argv, "-pub_file") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            pubfile = *(++argv);
        }
        else if (Strcmp(*argv, "-string") == 0)
        {
            if (--argc < 1)
            {
                goto bad;
            }
            string = *(++argv);
        }
#ifdef NO_SOFTWARE_CRYPTO
        else if (Strcmp(*argv, "-no_fips140") == 0)
        {
            operating_mode = NON_FIPS140_MODE;
        }
        else if (Strcmp(*argv, "-fips140_ssl") == 0)
        {
            operating_mode = FIPS140_SSL_MODE;
        }
#endif /* NO_SOFTWARE_CRYPTO */
        else if (Strcmp(*argv, "-help") == 0)
        {
            goto bad;
        }
        else
        {
            BIO_printf(bio_err, "Unknown option %s\n", *argv);
            goto bad;
        }
        argc--;
        argv++;
    }

    /* Validate the command line options */
    if ((string == NULL) || (pubfile == NULL) || (privfile == NULL))
    {
        BIO_printf(bio_err, "No keys or data to sign or verify with\n");
        goto bad;
    }

    if (0)
    {
        char **pp;
bad:
        for (pp = r_sign_usage; (*pp != NULL); pp++)
        {
            BIO_printf(bio_err, *pp);
        }
        goto end;
    }

   /*
    * Create the library context to provide access to all configurable aspects
    * of the library
    */
#ifdef NO_SOFTWARE_CRYPTO
    /*
     * For FIPS140 shared library builds set the operating mode required
     * first
     */
    switch (operating_mode)
    {

    case FIPS140_MODE:
        CRYPTOC_FIPS140_enable_fips140_operating_mode();
        break;

    case NON_FIPS140_MODE:
        CRYPTOC_FIPS140_enable_non_fips140_operating_mode();
        break;

    case FIPS140_SSL_MODE:
        CRYPTOC_FIPS140_enable_fips140_ssl_operating_mode();
        break;
    }
#endif /* NO_SOFTWARE_CRYPTO */
    if ((ret = PRODUCT_LIBRARY_NEW(res_list, R_RES_FLAG_DEF, &lib_ctx)) !=
        R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create library context\n");
        goto end;
    }

   /* Create a new cryptographic context */
    if ((ret = R_CR_CTX_new(lib_ctx, R_RES_FLAG_DEF, &ctx)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create crypto context\n");
        goto end;
    }

   /* Create a new asymmetric key context */
    if ((ret = R_PKEY_CTX_new(lib_ctx, R_RES_FLAG_DEF, pkey_type,
        &pkey_ctx)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create key context\n");
        goto end;
    }

   /* Sign the data and display the resulting signature */
    if ((ret = sign_data(bio_err, ctx, pkey_ctx, pkey_type, alg, privfile,
        string, buf, &len)) != R_ERROR_NONE)
    {
        goto end;
    }

    /* Print the signature */
    BIO_printf(bio_out, "SIGNATURE DATA:\n");
    BIO_dump(bio_out, buf, len);

   /* Verify the signature on the data */
    if ((ret = verify_data(bio_err, ctx, pkey_ctx, pkey_type, alg, pubfile,
        string, buf, len, &result)) != R_ERROR_NONE)
    {
        goto end;
    }

    /* Print the result */
    if (result == 0)
    {
        BIO_printf(bio_out, "Signature verification passed\n");
    }
    else
    {
        BIO_printf(bio_out, "Signature verification failed\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))
    {
#ifndef NO_CRYPTO_ERR
        BIO_printf(bio_err, "ERROR: (%d) %s\n", ret,
            R_LIB_CTX_get_error_string(lib_ctx, R_RES_MOD_ID_LIBRARY, ret));
#else /* NO_CRYPTO_ERR */
        BIO_printf(bio_err, "ERROR: (%d)\n", ret);
#endif /* NO_CRYPTO_ERR */
    }

    if (ctx != NULL)
    {
        R_CR_CTX_free(ctx);
    }

    if (pkey_ctx != NULL)
    {
        R_PKEY_CTX_free(pkey_ctx);
    }

    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }

    if (bio_err != NULL)
    {
        BIO_free(bio_err);
    }

    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }

    return(R_ERROR_EXIT_CODE(ret));
}

/*
 * Digests data for signing algorithms that do not perform this operation
 * internally.
 *
 * @param bio_err [In]      A BIO for error messages.
 * @param ctx     [In]      A reference to the cryptographic context.
 * @param alg     [In]      The algorithm to use for digesting.
 * @param string  [In]      The string data that is to be digested.
 * @param strlen  [In]      The length of the string data.
 * @param obuf    [In, Out] The output buffer for the digest.
 * @param olen    [In, Out] The length of the digest written to <i>obuf</i>.
 *
 * @returns  R_ERROR_NONE indicates success.<br>
 *           See @ref R_ERROR_IDS for valid values.
 */
static int digest_data(BIO *bio_err, R_CR_CTX *ctx, R_CR_ALG_ID alg,
    char *string, unsigned int strlen, unsigned char *obuf,
    unsigned int *olen)
{
    int ret = R_ERROR_NONE;
    R_CR *dgst_obj = NULL;

    if ((ret = R_CR_new(ctx, R_CR_TYPE_DIGEST, alg, R_CR_SUB_NONE,
        &dgst_obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create digest object\n");
    }
    else if ((ret = R_CR_digest_init(dgst_obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to initialize digest object\n");
    }
    else if ((ret = R_CR_digest(dgst_obj, (unsigned char*)string, strlen, obuf,
        olen)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to digest data\n");
    }

    if (dgst_obj != NULL)
    {
        R_CR_free(dgst_obj);
    }

    return(ret);
}

/*
 * Loads a private key from a file and signs a string with a specified
 * algorithm.
 *
 * @param bio_err  [In]      A BIO for error messages.
 * @param ctx      [In]      A reference to the cryptographic context.
 * @param pkey_ctx [In]      A reference to the asymmetric key context.
 * @param alg      [In]      The algorithm to use for signing.
 * @param privfile [In]      The name of the file where the private key is
 *                           stored in binary format.
 * @param string   [In]      The string data that is to be signed.
 * @param obuf     [In, Out] The output buffer for the signature.
 * @param olen     [In, Out] The length of the signature written to
 *                           <i>obuf</i>.
 *
 * @returns  R_ERROR_NONE indicates success.<br>
 *           See @ref R_ERROR_IDS for valid values.
 */
static int sign_data(BIO *bio_err, R_CR_CTX *ctx, R_PKEY_CTX *pkey_ctx,
    R_PKEY_TYPE pkey_type, R_CR_ALG_ID alg, char *privfile, char *string,
    unsigned char *obuf, unsigned int *olen)
{
    int           ret        = R_ERROR_NONE;
    R_CR          *sign_obj   = NULL;
    R_PKEY        *pkey       = NULL;
    unsigned char md[R_CR_DIGEST_MAX_LEN];
    unsigned char *data;
    unsigned int  md_len = R_CR_DIGEST_MAX_LEN;
    unsigned char data_len;

    /*
     * Load the private key data from a file into an R_PKEY. The key
     * must be stored in binary format.
     */
    if ((ret = R_PKEY_from_file(pkey_ctx, &pkey, privfile, pkey_type,
        R_FORMAT_BINARY)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to read key from file %s\n", privfile);
        goto end;
    }

   /*
    * Create a new signing cryptographic object using the specified
    * algorithm
    */
    if ((ret = R_CR_new(ctx, R_CR_TYPE_SIGNATURE, alg, R_CR_SUB_SIGN,
        &sign_obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create crypto object\n");
        goto end;
    }

   /* Load the private key against the cryptographic object */
    if ((ret = R_CR_sign_init(sign_obj, pkey)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to initialize crypto object\n");
        goto end;
    }

    /* For straight DSA we pass in the pre-digested data */
    if (alg == R_CR_ID_DSA)
    {
        if ((ret = digest_data(bio_err, ctx, R_CR_ID_SHA1,
            string, Strlen(string), md, &md_len)) != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to digest signature data\n");
            goto end;
        }

        data = md;
        data_len = md_len;
    }
    else
    {
        data = (unsigned char *)string;
        data_len = Strlen(string);
    }

   /* Sign the data */
    if ((ret = R_CR_sign(sign_obj, data, data_len, obuf, olen)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to sign data\n");
        goto end;
    }

end:

    /*
     * Clean up. Destroy any dynamically allocated objects and return an
     * error code.
     */
    if (sign_obj != NULL)
    {
        R_CR_free(sign_obj);
    }

    if (pkey != NULL)
    {
        R_PKEY_free(pkey);
    }

    return (ret);
}

/*
 * Verifies the signed data <i>dbuf</i> using the public key found in the
 * file <i>pubfile</i> and indicates the signature is valid.
 *
 * @param bio_err   [In]  A BIO for error messages.
 * @param ctx       [In]  A reference to the cryptographic context.
 * @param pkey_ctx  [In]  A reference to the asymmetric key context.
 * @param alg       [In]  The algorithm used to sign the data.
 * @param pubfile   [In]  The name of the file where the public key is stored.
 * @param string    [In]  The original message that was signed.
 * @param dbuf      [In]  The signature previously generated from
 *                        <i>string</i>.
 * @param dlen      [In]  The length of the signature.
 * @param result    [Out] The result of the verification process.
 *
 * @returns  R_ERROR_NONE indicates success.<br>
 *           See @ref R_ERROR_IDS for valid values.
 */
static int verify_data(BIO *bio_err, R_CR_CTX *ctx, R_PKEY_CTX *pkey_ctx,
    R_PKEY_TYPE pkey_type, R_CR_ALG_ID alg, char *pubfile, char *string,
    unsigned char *dbuf, unsigned int dlen, int *result)
{
    int           ret         = R_ERROR_NONE;
    R_CR          *vfy_obj    = NULL;
    R_PKEY        *pkey       = NULL;
    unsigned char *tbuf       = NULL;
    unsigned char *tmp;
    unsigned char md[R_CR_DIGEST_MAX_LEN];
    unsigned char *data;
    unsigned int  md_len = R_CR_DIGEST_MAX_LEN;
    unsigned int  data_len;
    unsigned int  tlen;
    unsigned int  consumed_len;

    /* Read the key from a file and store in binary format */
    if ((ret = data_from_file(bio_err, pubfile, &tbuf, &tlen)) != R_ERROR_NONE)
    {
        goto end;
    }

    /*
     * Create an R_PKEY object from the binary key data. Extract the public key
     * information from the binary data and load into a key object. This
     * function will move the data pointer to the end of the buffer, so pass in
     * a copy in case the key data is required later.
     */
    tmp = tbuf;
    if ((ret = R_PKEY_from_public_key_binary(pkey_ctx, R_PKEY_FL_BY_REFERENCE,
        pkey_type, tlen, (const unsigned char *)tmp,
        &consumed_len, &pkey )) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to read key from binary\n");
        goto end;
    }

    /*
     * Create a new signature verification cryptographic object
     * using the specified signing algorithm
     */
    if ((ret = R_CR_new(ctx, R_CR_TYPE_SIGNATURE, alg, R_CR_SUB_VERIFY,
        &vfy_obj)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create crypto object\n");
        goto end;
    }

    /* Load the public key against the cryptographic object */
    if ((ret = R_CR_verify_init(vfy_obj, pkey)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to initialize crypto object\n");
        goto end;
    }

    /* For straight DSA we pass in the pre-digested data */
    if (alg == R_CR_ID_DSA)
    {
        if ((ret = digest_data(bio_err, ctx, R_CR_ID_SHA1,
            string, Strlen(string), md, &md_len)) != R_ERROR_NONE)
        {
            BIO_printf(bio_err, "Unable to digest signature data\n");
            goto end;
        }

        data = md;
        data_len = md_len;
    }
    else
    {
        data = (unsigned char *)string;
        data_len = Strlen(string);
    }

    /* Verify that the data can be recovered from the original signature */
    if ((ret = R_CR_verify(vfy_obj, data, data_len, dbuf, dlen,
        result)) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to verify data\n");
        goto end;
    }

end:

    /*
     * Clean up. Destroy any dynamically allocated objects and return an
     * error code.
     */

    if (vfy_obj != NULL)
    {
        R_CR_free(vfy_obj);
    }

    if (pkey != NULL)
    {
        R_PKEY_free(pkey);
    }

    if (tbuf != NULL)
    {
        Free(tbuf);
    }
    return (ret);
}

/*
 * Reads binary data from a file into an allocated buffer.
 *
 * @param bio_err   [In]  A BIO for error messages.
 * @param filename  [In]  The name of the file to read.
 * @param dbuf      [Out] The allocated data buffer.
 * @param dlen      [Out] The length of the data in <i>dbuf</i>.
 *
 * @returns R_ERROR_NONE indicates success.<br>
 *          See @ref R_ERROR_IDS for valid values.
 */
static int data_from_file(BIO *bio_err, char *filename, unsigned char **dbuf,
    unsigned int *dlen)
{
    int             ret         = R_ERROR_NONE;
    unsigned int    tlen;
    int             l;
    BIO             *bio_file   = NULL;
    unsigned char   buf[512];

    /* Create a new BIO for the binary data file */
    if ((bio_file = BIO_new_file(filename, "rb")) == NULL)
    {
        BIO_printf(bio_err, "Unable to read from file: %s\n", filename);
        ret = R_ERROR_ALLOC_FAILURE;
        goto end;
    }

    /* Calculate the size of the data in the file */
    tlen = 0;

    while ((l = BIO_read(bio_file, (char *) buf, sizeof(buf))) > 0)
    {
        tlen += l;
    }

    /* Allocate a buffer for the data */
    if (((*dbuf) = (unsigned char *) Malloc(tlen)) == NULL)
    {
        BIO_printf(bio_err, "Failed to allocated memory for binary data\n");
        ret = R_ERROR_ALLOC_FAILURE;
        goto end;
    }

    /* Reset the BIO to the start of the file */
    BIO_reset(bio_file);

    /* Read the data into the newly allocated buffer */
    BIO_read(bio_file, (char *) (*dbuf), tlen);

    /* Return the length of the data */
    (*dlen) = tlen;

end:

    /* Destroy any dynamically allocated objects and return an error code */
    if (bio_file != NULL)
    {
        BIO_free(bio_file);
    }

    return (ret);
}

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