root/usr/src/lib/pkcs11/pkcs11_tpm/common/mech_rsa.c
/*
 * The Initial Developer of the Original Code is International
 * Business Machines Corporation. Portions created by IBM
 * Corporation are Copyright (C) 2005 International Business
 * Machines Corporation. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the Common Public License as published by
 * IBM Corporation; either version 1 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Common Public License for more details.
 *
 * You should have received a copy of the Common Public License
 * along with this program; if not, a copy can be viewed at
 * http://www.opensource.org/licenses/cpl1.0.php.
 */
/* (C) COPYRIGHT International Business Machines Corp. 2001, 2002, 2005 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "tpmtok_int.h"

extern CK_RV
token_specific_rsa_verify_recover(
        TSS_HCONTEXT    hContext,
        CK_BYTE         *in_data,
        CK_ULONG        in_data_len,
        CK_BYTE         *out_data,
        CK_ULONG        *out_data_len,
        OBJECT          *key_obj);

CK_RV
ckm_rsa_key_pair_gen(TSS_HCONTEXT hContext,
        TEMPLATE  * publ_tmpl,
        TEMPLATE  * priv_tmpl)
{
        CK_RV           rc;

        rc = token_specific.t_rsa_generate_keypair(
            hContext, publ_tmpl, priv_tmpl);

        return (rc);
}

static CK_RV
ckm_rsa_encrypt(
        TSS_HCONTEXT hContext,
        CK_BYTE   * in_data,
        CK_ULONG    in_data_len,
        CK_BYTE   * out_data,
        CK_ULONG  * out_data_len,
        OBJECT    * key_obj)
{
        CK_ATTRIBUTE    * attr    = NULL;
        CK_OBJECT_CLASS keyclass;
        CK_RV           rc;

        rc = template_attribute_find(key_obj->template, CKA_CLASS, &attr);
        if (rc == FALSE) {
                return (CKR_FUNCTION_FAILED);
        } else
                keyclass = *(CK_OBJECT_CLASS *)attr->pValue;

        if (keyclass != CKO_PUBLIC_KEY) {
                return (CKR_FUNCTION_FAILED);
        }

        rc = token_specific.t_rsa_encrypt(hContext,
            in_data, in_data_len,
            out_data, out_data_len, key_obj);

        return (rc);
}

static CK_RV
ckm_rsa_decrypt(
        TSS_HCONTEXT hContext,
        CK_BYTE   * in_data,
        CK_ULONG    in_data_len,
        CK_BYTE   * out_data,
        CK_ULONG  * out_data_len,
        OBJECT    * key_obj) {
        CK_ATTRIBUTE    * attr  = NULL;
        CK_OBJECT_CLASS keyclass;
        CK_RV           rc;


        rc = template_attribute_find(key_obj->template, CKA_CLASS, &attr);
        if (rc == FALSE) {
                return (CKR_FUNCTION_FAILED);
        }
        else
                keyclass = *(CK_OBJECT_CLASS *)attr->pValue;

        // this had better be a private key
        //
        if (keyclass != CKO_PRIVATE_KEY) {
                return (CKR_FUNCTION_FAILED);
        }
        rc = token_specific.t_rsa_decrypt(hContext,
            in_data, in_data_len,
            out_data, out_data_len, key_obj);

        return (rc);
}

static CK_RV
ckm_rsa_sign(
        TSS_HCONTEXT hContext,
        CK_BYTE   * in_data,
        CK_ULONG    in_data_len,
        CK_BYTE   * out_data,
        CK_ULONG  * out_data_len,
        OBJECT    * key_obj) {
        CK_ATTRIBUTE    * attr  = NULL;
        CK_OBJECT_CLASS keyclass;
        CK_RV           rc;


        rc = template_attribute_find(key_obj->template, CKA_CLASS, &attr);
        if (rc == FALSE) {
                return (CKR_FUNCTION_FAILED);
        } else {
                keyclass = *(CK_OBJECT_CLASS *)attr->pValue;
        }

        if (keyclass != CKO_PRIVATE_KEY) {
                return (CKR_FUNCTION_FAILED);
        }
        rc = token_specific.t_rsa_sign(
            hContext, in_data, in_data_len, out_data,
            out_data_len, key_obj);

        return (rc);
}

static CK_RV
ckm_rsa_verify(
        TSS_HCONTEXT hContext,
        CK_BYTE   * in_data,
        CK_ULONG    in_data_len,
        CK_BYTE   * out_data,
        CK_ULONG    out_data_len,
        OBJECT    * key_obj) {
        CK_ATTRIBUTE    * attr  = NULL;
        CK_OBJECT_CLASS keyclass;
        CK_RV           rc;


        rc = template_attribute_find(key_obj->template, CKA_CLASS, &attr);
        if (rc == FALSE) {
                return (CKR_FUNCTION_FAILED);
        }
        else
                keyclass = *(CK_OBJECT_CLASS *)attr->pValue;

        if (keyclass != CKO_PUBLIC_KEY) {
                return (CKR_FUNCTION_FAILED);
        }
        rc = token_specific.t_rsa_verify(hContext,
            in_data, in_data_len, out_data,
            out_data_len, key_obj);

        return (rc);
}

/*ARGSUSED*/
CK_RV
rsa_pkcs_encrypt(SESSION           *sess,
        CK_BBOOL           length_only,
        ENCR_DECR_CONTEXT *ctx,
        CK_BYTE    *in_data,
        CK_ULONG           in_data_len,
        CK_BYTE    *out_data,
        CK_ULONG          *out_data_len)
{
        OBJECT    *key_obj  = NULL;
        CK_ATTRIBUTE    *attr   = NULL;
        CK_ULONG         modulus_bytes;
        CK_BBOOL         flag;
        CK_RV       rc;


        rc = object_mgr_find_in_map1(sess->hContext, ctx->key, &key_obj);
        if (rc != CKR_OK) {
                return (rc);
        }
        flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
        if (flag == FALSE) {
                return (CKR_FUNCTION_FAILED);
        } else {
                modulus_bytes = attr->ulValueLen;
        }

        if (length_only == TRUE) {
                *out_data_len = modulus_bytes;
                return (CKR_OK);
        }

        if (*out_data_len < modulus_bytes) {
                *out_data_len = modulus_bytes;
                return (CKR_BUFFER_TOO_SMALL);
        }

        rc = ckm_rsa_encrypt(sess->hContext, in_data, in_data_len, out_data,
            out_data_len, key_obj);
        return (rc);
}

/*ARGSUSED*/
CK_RV
rsa_pkcs_decrypt(SESSION           *sess,
        CK_BBOOL           length_only,
        ENCR_DECR_CONTEXT *ctx,
        CK_BYTE    *in_data,
        CK_ULONG           in_data_len,
        CK_BYTE    *out_data,
        CK_ULONG          *out_data_len)
{
        OBJECT    *key_obj  = NULL;
        CK_ATTRIBUTE    *attr   = NULL;
        CK_ULONG         modulus_bytes;
        CK_BBOOL         flag;
        CK_RV       rc;


        rc = object_mgr_find_in_map1(sess->hContext, ctx->key, &key_obj);
        if (rc != CKR_OK) {
                return (rc);
        }
        flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
        if (flag == FALSE)
                return (CKR_FUNCTION_FAILED);
        else
                modulus_bytes = attr->ulValueLen;

        if (in_data_len != modulus_bytes) {
                return (CKR_ENCRYPTED_DATA_LEN_RANGE);
        }
        if (length_only == TRUE) {
                *out_data_len = modulus_bytes - 11;
                return (CKR_OK);
        }

        rc = ckm_rsa_decrypt(sess->hContext, in_data,
            modulus_bytes, out_data,
            out_data_len, key_obj);

        if (rc == CKR_DATA_LEN_RANGE) {
                return (CKR_ENCRYPTED_DATA_LEN_RANGE);
        }
        return (rc);
}

CK_RV
rsa_pkcs_sign(SESSION           *sess,
        CK_BBOOL                length_only,
        SIGN_VERIFY_CONTEXT *ctx,
        CK_BYTE         *in_data,
        CK_ULONG                in_data_len,
        CK_BYTE         *out_data,
        CK_ULONG            *out_data_len)
{
        OBJECT    *key_obj   = NULL;
        CK_ATTRIBUTE    *attr   = NULL;
        CK_ULONG         modulus_bytes;
        CK_BBOOL         flag;
        CK_RV       rc;


        if (! sess || ! ctx || ! out_data_len) {
                return (CKR_FUNCTION_FAILED);
        }
        rc = object_mgr_find_in_map1(sess->hContext, ctx->key, &key_obj);
        if (rc != CKR_OK) {
                return (rc);
        }
        flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
        if (flag == FALSE)
                return (CKR_FUNCTION_FAILED);
        else
                modulus_bytes = attr->ulValueLen;

        if (length_only == TRUE) {
                *out_data_len = modulus_bytes;
                return (CKR_OK);
        }

        if (*out_data_len < modulus_bytes) {
                *out_data_len = modulus_bytes;
                return (CKR_BUFFER_TOO_SMALL);
        }

        rc = ckm_rsa_sign(sess->hContext, in_data, in_data_len, out_data,
            out_data_len, key_obj);
        return (rc);
}

/*ARGSUSED*/
CK_RV
rsa_pkcs_verify(SESSION         * sess,
        SIGN_VERIFY_CONTEXT * ctx,
        CK_BYTE         * in_data,
        CK_ULONG                in_data_len,
        CK_BYTE         * signature,
        CK_ULONG                sig_len)
{
        OBJECT    *key_obj  = NULL;
        CK_ATTRIBUTE    *attr   = NULL;
        CK_ULONG         modulus_bytes;
        CK_BBOOL         flag;
        CK_RV       rc;

        rc = object_mgr_find_in_map1(sess->hContext, ctx->key, &key_obj);
        if (rc != CKR_OK) {
                return (rc);
        }
        flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
        if (flag == FALSE) {
                return (CKR_FUNCTION_FAILED);
        }
        else
                modulus_bytes = attr->ulValueLen;

        // check input data length restrictions
        //
        if (sig_len != modulus_bytes) {
                return (CKR_SIGNATURE_LEN_RANGE);
        }
        // verify is a public key operation --> encrypt
        //
        rc = ckm_rsa_verify(sess->hContext, in_data, in_data_len, signature,
            sig_len, key_obj);

        return (rc);
}

CK_RV
rsa_pkcs_verify_recover(SESSION *sess,
        CK_BBOOL        length_only,
        SIGN_VERIFY_CONTEXT     *ctx,
        CK_BYTE         *signature,
        CK_ULONG        sig_len,
        CK_BYTE         *out_data,
        CK_ULONG        *out_data_len)
{
        OBJECT          *key_obj  = NULL;
        CK_ATTRIBUTE    *attr   = NULL;
        CK_ULONG        modulus_bytes;
        CK_BBOOL        flag;
        CK_RV           rc;

        if (! sess || ! ctx || ! out_data_len) {
                return (CKR_FUNCTION_FAILED);
        }
        rc = object_mgr_find_in_map1(sess->hContext, ctx->key, &key_obj);
        if (rc != CKR_OK) {
                return (rc);
        }
        flag = template_attribute_find(key_obj->template, CKA_MODULUS, &attr);
        if (flag == FALSE) {
                return (CKR_FUNCTION_FAILED);
        }
        else
                modulus_bytes = attr->ulValueLen;

        if (sig_len != modulus_bytes) {
                return (CKR_SIGNATURE_LEN_RANGE);
        }
        if (length_only == TRUE) {
                *out_data_len = modulus_bytes;
                return (CKR_OK);
        }

        rc = token_specific_rsa_verify_recover(sess->hContext,
            signature, modulus_bytes, out_data, out_data_len, key_obj);

        return (rc);
}

CK_RV
rsa_hash_pkcs_sign(SESSION              * sess,
        CK_BBOOL                length_only,
        SIGN_VERIFY_CONTEXT  * ctx,
        CK_BYTE         * in_data,
        CK_ULONG                in_data_len,
        CK_BYTE         * signature,
        CK_ULONG                * sig_len)
{
        CK_BYTE     * ber_data  = NULL;
        CK_BYTE     * octet_str = NULL;
        CK_BYTE     * oid       = NULL;
        CK_BYTE     * tmp       = NULL;

        CK_ULONG                buf1[16];

        CK_BYTE         hash[SHA1_DIGEST_LENGTH];
        DIGEST_CONTEXT  digest_ctx;
        SIGN_VERIFY_CONTEXT  sign_ctx;
        CK_MECHANISM     digest_mech;
        CK_MECHANISM     sign_mech;
        CK_ULONG        ber_data_len, hash_len, octet_str_len, oid_len;
        CK_RV           rc;

        if (! sess || ! ctx || ! in_data) {
                return (CKR_FUNCTION_FAILED);
        }
        (void) memset(&digest_ctx, 0x0, sizeof (digest_ctx));
        (void) memset(&sign_ctx,   0x0, sizeof (sign_ctx));

        if (ctx->mech.mechanism == CKM_MD5_RSA_PKCS) {
                digest_mech.mechanism   = CKM_MD5;
                oid = ber_AlgMd5;
                oid_len = ber_AlgMd5Len;
                hash_len = MD5_DIGEST_LENGTH;
        } else {
                digest_mech.mechanism   = CKM_SHA_1;
                oid = ber_AlgSha1;
                oid_len = ber_AlgSha1Len;
                hash_len = SHA1_DIGEST_LENGTH;
        }

        digest_mech.ulParameterLen = 0;
        digest_mech.pParameter  = NULL;

        rc = digest_mgr_init(sess, &digest_ctx, &digest_mech);
        if (rc != CKR_OK) {
                goto error;
        }
        rc = digest_mgr_digest(sess, length_only, &digest_ctx, in_data,
            in_data_len, hash, &hash_len);
        if (rc != CKR_OK)
                goto error;

        rc = ber_encode_OCTET_STRING(FALSE, &octet_str, &octet_str_len,
            hash, hash_len);
        if (rc != CKR_OK) {
                goto error;
        }
        tmp = (CK_BYTE *)buf1;
        (void) memcpy(tmp,         oid, oid_len);
        (void) memcpy(tmp + oid_len, octet_str, octet_str_len);

        rc = ber_encode_SEQUENCE(FALSE, &ber_data, &ber_data_len,
            tmp, (oid_len + octet_str_len));
        if (rc != CKR_OK)
                goto error;

        sign_mech.mechanism     = CKM_RSA_PKCS;
        sign_mech.ulParameterLen = 0;
        sign_mech.pParameter    = NULL;

        rc = sign_mgr_init(sess, &sign_ctx, &sign_mech, FALSE, ctx->key);
        if (rc != CKR_OK)
                goto error;

        rc = sign_mgr_sign(sess, length_only, &sign_ctx, ber_data,
            ber_data_len, signature, sig_len);

error:
        if (octet_str) free(octet_str);
        if (ber_data)  free(ber_data);
        (void) digest_mgr_cleanup(&digest_ctx);
        (void) sign_mgr_cleanup(&sign_ctx);
        return (rc);
}

CK_RV
rsa_hash_pkcs_sign_update(
        SESSION         * sess,
        SIGN_VERIFY_CONTEXT  * ctx,
        CK_BYTE         * in_data,
        CK_ULONG                in_data_len)
{
        RSA_DIGEST_CONTEXT  * context = NULL;
        CK_MECHANISM      digest_mech;
        CK_RV            rc;

        if (! sess || ! ctx || ! in_data)
                return (CKR_FUNCTION_FAILED);

        context = (RSA_DIGEST_CONTEXT *)ctx->context;

        if (context->flag == FALSE) {
                if (ctx->mech.mechanism == CKM_MD5_RSA_PKCS)
                        digest_mech.mechanism = CKM_MD5;
                else
                        digest_mech.mechanism = CKM_SHA_1;

                digest_mech.ulParameterLen = 0;
                digest_mech.pParameter  = NULL;

                rc = digest_mgr_init(sess, &context->hash_context,
                    &digest_mech);
                if (rc != CKR_OK) {
                        goto error;
                }
                context->flag = TRUE;
        }

        rc = digest_mgr_digest_update(sess, &context->hash_context,
            in_data, in_data_len);
        if (rc != CKR_OK) {
                goto error;
        }
        return (CKR_OK);
error:
        (void) digest_mgr_cleanup(&context->hash_context);
        return (rc);
}

CK_RV
rsa_hash_pkcs_verify(SESSION            * sess,
        SIGN_VERIFY_CONTEXT  * ctx,
        CK_BYTE         * in_data,
        CK_ULONG                in_data_len,
        CK_BYTE         * signature,
        CK_ULONG                sig_len)
{
        CK_BYTE     * ber_data  = NULL;
        CK_BYTE     * octet_str = NULL;
        CK_BYTE     * oid       = NULL;
        CK_BYTE     * tmp       = NULL;

        CK_ULONG        buf1[16];
        CK_BYTE         hash[SHA1_DIGEST_LENGTH];
        DIGEST_CONTEXT  digest_ctx;
        SIGN_VERIFY_CONTEXT  verify_ctx;
        CK_MECHANISM     digest_mech;
        CK_MECHANISM     verify_mech;
        CK_ULONG        ber_data_len, hash_len, octet_str_len, oid_len;
        CK_RV           rc;

        if (! sess || ! ctx || ! in_data) {
                return (CKR_FUNCTION_FAILED);
        }
        (void) memset(&digest_ctx, 0x0, sizeof (digest_ctx));
        (void) memset(&verify_ctx, 0x0, sizeof (verify_ctx));

        if (ctx->mech.mechanism == CKM_MD5_RSA_PKCS) {
                digest_mech.mechanism   = CKM_MD5;
                oid = ber_AlgMd5;
                oid_len = ber_AlgMd5Len;
                hash_len = MD5_DIGEST_LENGTH;
        } else {
                digest_mech.mechanism   = CKM_SHA_1;
                oid = ber_AlgSha1;
                oid_len = ber_AlgSha1Len;
                hash_len = SHA1_DIGEST_LENGTH;
        }

        digest_mech.ulParameterLen = 0;
        digest_mech.pParameter  = NULL;

        rc = digest_mgr_init(sess, &digest_ctx, &digest_mech);
        if (rc != CKR_OK) {
                goto done;
        }
        rc = digest_mgr_digest(sess, FALSE, &digest_ctx, in_data,
            in_data_len, hash, &hash_len);
        if (rc != CKR_OK) {
                goto done;
        }
        rc = ber_encode_OCTET_STRING(FALSE, &octet_str, &octet_str_len,
            hash, hash_len);
        if (rc != CKR_OK)
                goto done;
        tmp = (CK_BYTE *)buf1;
        (void) memcpy(tmp,   oid, oid_len);
        (void) memcpy(tmp + oid_len, octet_str, octet_str_len);

        rc = ber_encode_SEQUENCE(FALSE, &ber_data, &ber_data_len, tmp,
            (oid_len + octet_str_len));
        if (rc != CKR_OK) {
                goto done;
        }

        verify_mech.mechanism   = CKM_RSA_PKCS;
        verify_mech.ulParameterLen = 0;
        verify_mech.pParameter  = NULL;

        rc = verify_mgr_init(sess, &verify_ctx, &verify_mech, FALSE, ctx->key);
        if (rc != CKR_OK) {
                goto done;
        }
        rc = verify_mgr_verify(sess, &verify_ctx, ber_data,
            ber_data_len, signature, sig_len);
done:
        if (octet_str) free(octet_str);
        if (ber_data)  free(ber_data);

        (void) digest_mgr_cleanup(&digest_ctx);
        (void) sign_mgr_cleanup(&verify_ctx);
        return (rc);
}

CK_RV
rsa_hash_pkcs_verify_update(SESSION             * sess,
        SIGN_VERIFY_CONTEXT  * ctx,
        CK_BYTE         *in_data,
        CK_ULONG        in_data_len)
{
        RSA_DIGEST_CONTEXT  * context = NULL;
        CK_MECHANISM      digest_mech;
        CK_RV            rc;

        if (! sess || ! ctx || ! in_data) {
                return (CKR_FUNCTION_FAILED);
        }
        context = (RSA_DIGEST_CONTEXT *)ctx->context;

        if (context->flag == FALSE) {
                if (ctx->mech.mechanism == CKM_MD5_RSA_PKCS)
                        digest_mech.mechanism = CKM_MD5;
                else
                        digest_mech.mechanism = CKM_SHA_1;

                digest_mech.ulParameterLen = 0;
                digest_mech.pParameter  = NULL;

                rc = digest_mgr_init(sess, &context->hash_context,
                    &digest_mech);
                if (rc != CKR_OK)
                        goto error;
                context->flag = TRUE;
        }

        rc = digest_mgr_digest_update(sess, &context->hash_context,
            in_data, in_data_len);
        if (rc != CKR_OK)
                goto error;
        return (CKR_OK);
error:
        (void) digest_mgr_cleanup(&context->hash_context);
        return (rc);
}

CK_RV
rsa_hash_pkcs_sign_final(SESSION                * sess,
        CK_BBOOL        length_only,
        SIGN_VERIFY_CONTEXT  * ctx,
        CK_BYTE         * signature,
        CK_ULONG        * sig_len)
{
        CK_BYTE     * ber_data  = NULL;
        CK_BYTE     * octet_str = NULL;
        CK_BYTE     * oid       = NULL;
        CK_BYTE     * tmp       = NULL;

        CK_ULONG buf1[16];
        CK_BYTE         hash[SHA1_DIGEST_LENGTH];
        RSA_DIGEST_CONTEXT  * context = NULL;
        CK_ULONG        ber_data_len, hash_len, octet_str_len, oid_len;
        CK_MECHANISM  sign_mech;
        SIGN_VERIFY_CONTEXT   sign_ctx;
        CK_RV            rc;

        if (! sess || ! ctx || ! sig_len) {
                return (CKR_FUNCTION_FAILED);
        }

        if (ctx->mech.mechanism == CKM_MD5_RSA_PKCS) {
                oid = ber_AlgMd5;
                oid_len = ber_AlgMd5Len;
                hash_len = MD5_DIGEST_LENGTH;
        } else {
                oid = ber_AlgSha1;
                oid_len = ber_AlgSha1Len;
                hash_len = SHA1_DIGEST_LENGTH;
        }

        (void) memset(&sign_ctx, 0x0, sizeof (sign_ctx));

        context = (RSA_DIGEST_CONTEXT *)ctx->context;

        rc = digest_mgr_digest_final(sess,
            &context->hash_context, hash, &hash_len);
        if (rc != CKR_OK) {
                goto done;
        }

        rc = ber_encode_OCTET_STRING(FALSE, &octet_str, &octet_str_len,
            hash, hash_len);
        if (rc != CKR_OK) {
                goto done;
        }
        tmp = (CK_BYTE *)buf1;
        (void) memcpy(tmp,  oid, oid_len);
        (void) memcpy(tmp + oid_len, octet_str, octet_str_len);

        rc = ber_encode_SEQUENCE(FALSE, &ber_data, &ber_data_len,
            tmp, (oid_len + octet_str_len));
        if (rc != CKR_OK) {
                goto done;
        }
        sign_mech.mechanism     = CKM_RSA_PKCS;
        sign_mech.ulParameterLen = 0;
        sign_mech.pParameter    = NULL;

        rc = sign_mgr_init(sess, &sign_ctx, &sign_mech, FALSE, ctx->key);
        if (rc != CKR_OK) {
                goto done;
        }
        rc = sign_mgr_sign(sess, length_only, &sign_ctx, ber_data,
            ber_data_len, signature, sig_len);

        if (length_only == TRUE || rc == CKR_BUFFER_TOO_SMALL) {
                (void) sign_mgr_cleanup(&sign_ctx);
                return (rc);
        }

done:
        if (octet_str) free(octet_str);
        if (ber_data)  free(ber_data);

        (void) digest_mgr_cleanup(&context->hash_context);
        (void) sign_mgr_cleanup(&sign_ctx);
        return (rc);
}

CK_RV
rsa_hash_pkcs_verify_final(SESSION              * sess,
        SIGN_VERIFY_CONTEXT  * ctx,
        CK_BYTE         * signature,
        CK_ULONG                sig_len)
{
        CK_BYTE     * ber_data  = NULL;
        CK_BYTE     * octet_str = NULL;
        CK_BYTE     * oid       = NULL;
        CK_BYTE     * tmp       = NULL;

        CK_ULONG        buf1[16];
        CK_BYTE         hash[SHA1_DIGEST_LENGTH];
        RSA_DIGEST_CONTEXT  * context = NULL;
        CK_ULONG        ber_data_len, hash_len, octet_str_len, oid_len;
        CK_MECHANISM      verify_mech;
        SIGN_VERIFY_CONTEXT   verify_ctx;
        CK_RV            rc;

        if (! sess || ! ctx || ! signature) {
                return (CKR_FUNCTION_FAILED);
        }
        if (ctx->mech.mechanism == CKM_MD5_RSA_PKCS) {
                oid = ber_AlgMd5;
                oid_len = ber_AlgMd5Len;
                hash_len = MD5_DIGEST_LENGTH;
        } else {
                oid = ber_AlgSha1;
                oid_len = ber_AlgSha1Len;
                hash_len = SHA1_DIGEST_LENGTH;
        }

        (void) memset(&verify_ctx, 0x0, sizeof (verify_ctx));

        context = (RSA_DIGEST_CONTEXT *)ctx->context;

        rc = digest_mgr_digest_final(sess, &context->hash_context,
            hash, &hash_len);
        if (rc != CKR_OK) {
                goto done;
        }
        rc = ber_encode_OCTET_STRING(FALSE, &octet_str, &octet_str_len,
            hash, hash_len);
        if (rc != CKR_OK) {
                goto done;
        }
        tmp = (CK_BYTE *)buf1;
        (void) memcpy(tmp, oid, oid_len);
        (void) memcpy(tmp + oid_len, octet_str, octet_str_len);

        rc = ber_encode_SEQUENCE(FALSE, &ber_data, &ber_data_len,
            tmp, (oid_len + octet_str_len));
        if (rc != CKR_OK) {
                goto done;
        }
        verify_mech.mechanism   = CKM_RSA_PKCS;
        verify_mech.ulParameterLen = 0;
        verify_mech.pParameter  = NULL;

        rc = verify_mgr_init(sess, &verify_ctx, &verify_mech, FALSE, ctx->key);
        if (rc != CKR_OK) {
                goto done;
        }
        rc = verify_mgr_verify(sess, &verify_ctx, ber_data,
            ber_data_len, signature, sig_len);
done:
        if (octet_str) free(octet_str);
        if (ber_data)  free(ber_data);
        (void) digest_mgr_cleanup(&context->hash_context);
        (void) verify_mgr_cleanup(&verify_ctx);
        return (rc);
}