OpenSolaris_b135/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);
}