OpenSolaris_b135/lib/pkcs11/pkcs11_tpm/common/sign_mgr.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"

CK_RV
sign_mgr_init(SESSION		* sess,
	SIGN_VERIFY_CONTEXT    * ctx,
	CK_MECHANISM	   * mech,
	CK_BBOOL		 recover_mode,
	CK_OBJECT_HANDLE	 key)
{
	OBJECT	  * key_obj = NULL;
	CK_ATTRIBUTE    * attr    = NULL;
	CK_BYTE	 * ptr	= NULL;
	CK_KEY_TYPE	keytype;
	CK_OBJECT_CLASS   class;
	CK_BBOOL	  flag;
	CK_RV		rc;

	if (! sess || ! ctx) {
		return (CKR_FUNCTION_FAILED);
	}
	if (ctx->active != FALSE) {
		return (CKR_OPERATION_ACTIVE);
	}

	rc = object_mgr_find_in_map1(sess->hContext, key, &key_obj);
	if (rc != CKR_OK) {
		return (CKR_KEY_HANDLE_INVALID);
	}
	rc = template_attribute_find(key_obj->template, CKA_SIGN, &attr);
	if (rc == FALSE) {
		return (CKR_KEY_TYPE_INCONSISTENT);
	} else {
		flag = *(CK_BBOOL *)attr->pValue;
		if (flag != TRUE) {
			return (CKR_KEY_FUNCTION_NOT_PERMITTED);
		}
	}

	switch (mech->mechanism) {
		case CKM_RSA_PKCS:
		{
			rc = template_attribute_find(key_obj->template,
			    CKA_KEY_TYPE, &attr);
			if (rc == FALSE) {
				return (CKR_KEY_TYPE_INCONSISTENT);
			} else {
				keytype = *(CK_KEY_TYPE *)attr->pValue;
				if (keytype != CKK_RSA) {
					return (CKR_KEY_TYPE_INCONSISTENT);
				}
			}

			// must be a PRIVATE key
			//
			flag = template_attribute_find(key_obj->template,
			    CKA_CLASS, &attr);
			if (flag == FALSE) {
				return (CKR_KEY_TYPE_INCONSISTENT);
			}
			else
				class = *(CK_OBJECT_CLASS *)attr->pValue;

			if (class != CKO_PRIVATE_KEY) {
				return (CKR_KEY_TYPE_INCONSISTENT);
			}
			// PKCS #11 doesn't allow multi - part RSA operations
			//
			ctx->context_len = 0;
			ctx->context	= NULL;
		}
		break;
		case CKM_MD5_RSA_PKCS:
		case CKM_SHA1_RSA_PKCS:
		{
			rc = template_attribute_find(key_obj->template,
			    CKA_KEY_TYPE, &attr);
			if (rc == FALSE) {
				return (CKR_KEY_TYPE_INCONSISTENT);
			} else {
				keytype = *(CK_KEY_TYPE *)attr->pValue;
				if (keytype != CKK_RSA) {
					return (CKR_KEY_TYPE_INCONSISTENT);
				}
			}

			// must be a PRIVATE key operation
			//
			flag = template_attribute_find(key_obj->template,
			    CKA_CLASS, &attr);
			if (flag == FALSE) {
				return (CKR_FUNCTION_FAILED);
			}
			else
				class = *(CK_OBJECT_CLASS *)attr->pValue;

			if (class != CKO_PRIVATE_KEY) {
				return (CKR_FUNCTION_FAILED);
			}
			ctx->context_len = sizeof (RSA_DIGEST_CONTEXT);
			ctx->context = (CK_BYTE *)malloc(
			    sizeof (RSA_DIGEST_CONTEXT));
			if (! ctx->context) {
				return (CKR_HOST_MEMORY);
			}
			(void) memset(ctx->context, 0x0,
			    sizeof (RSA_DIGEST_CONTEXT));
		}
		break;
		case CKM_MD5_HMAC:
		case CKM_SHA_1_HMAC:
		{
			if (mech->ulParameterLen != 0) {
				return (CKR_MECHANISM_PARAM_INVALID);
			}
			rc = template_attribute_find(key_obj->template,
			    CKA_KEY_TYPE, &attr);
			if (rc == FALSE) {
				return (CKR_KEY_TYPE_INCONSISTENT);
			} else {
				keytype = *(CK_KEY_TYPE *)attr->pValue;
				if (keytype != CKK_GENERIC_SECRET) {
					return (CKR_KEY_TYPE_INCONSISTENT);
				}
			}

			// PKCS #11 doesn't allow multi - part HMAC operations
			//
			ctx->context_len = 0;
			ctx->context	= NULL;
		}
		break;

		case CKM_MD5_HMAC_GENERAL:
		case CKM_SHA_1_HMAC_GENERAL:
		{
			CK_MAC_GENERAL_PARAMS *param =
			    (CK_MAC_GENERAL_PARAMS *)mech->pParameter;

			if (mech->ulParameterLen !=
			    sizeof (CK_MAC_GENERAL_PARAMS)) {
				return (CKR_MECHANISM_PARAM_INVALID);
			}

			if ((mech->mechanism == CKM_MD5_HMAC_GENERAL) &&
			    (*param > 16)) {
				return (CKR_MECHANISM_PARAM_INVALID);
			}
			if ((mech->mechanism == CKM_SHA_1_HMAC_GENERAL) &&
			    (*param > 20)) {
				return (CKR_MECHANISM_PARAM_INVALID);
			}
			rc = template_attribute_find(key_obj->template,
			    CKA_KEY_TYPE, &attr);
			if (rc == FALSE) {
				return (CKR_KEY_TYPE_INCONSISTENT);
			} else {
				keytype = *(CK_KEY_TYPE *)attr->pValue;
				if (keytype != CKK_GENERIC_SECRET) {
					return (CKR_KEY_TYPE_INCONSISTENT);
				}
			}

			// PKCS #11 doesn't allow multi - part HMAC operations
			//
			ctx->context_len = 0;
			ctx->context	= NULL;
		}
		break;
		default:
			return (CKR_MECHANISM_INVALID);
	}

	if (mech->ulParameterLen > 0 && mech->pParameter == NULL)
		return (CKR_ARGUMENTS_BAD);

	if (mech->ulParameterLen > 0) {
		ptr = (CK_BYTE *)malloc(mech->ulParameterLen);
		if (! ptr) {
			return (CKR_HOST_MEMORY);
		}
		(void) memcpy(ptr, mech->pParameter, mech->ulParameterLen);
	}

	ctx->key		 = key;
	ctx->mech.ulParameterLen = mech->ulParameterLen;
	ctx->mech.mechanism	= mech->mechanism;
	ctx->mech.pParameter	= ptr;
	ctx->multi		= FALSE;
	ctx->active		= TRUE;
	ctx->recover		= recover_mode;

	return (CKR_OK);
}

CK_RV
sign_mgr_cleanup(SIGN_VERIFY_CONTEXT *ctx)
{
	if (! ctx) {
		return (CKR_FUNCTION_FAILED);
	}
	ctx->key		 = 0;
	ctx->mech.ulParameterLen = 0;
	ctx->mech.mechanism	= 0;
	ctx->multi		= FALSE;
	ctx->active		= FALSE;
	ctx->recover		= FALSE;
	ctx->context_len	 = 0;

	if (ctx->mech.pParameter) {
		free(ctx->mech.pParameter);
		ctx->mech.pParameter = NULL;
	}

	if (ctx->context) {
		free(ctx->context);
		ctx->context = NULL;
	}

	return (CKR_OK);
}

CK_RV
sign_mgr_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)
{
	if (! sess || ! ctx) {
		return (CKR_FUNCTION_FAILED);
	}
	if (ctx->active == FALSE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}
	if (ctx->recover == TRUE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}

	// if the caller just wants the signature length, there is no reason to
	// specify the input data.  I just need the input data length
	//
	if ((length_only == FALSE) && (! in_data || ! out_data)) {
		return (CKR_FUNCTION_FAILED);
	}
	if (ctx->multi == TRUE) {
		return (CKR_OPERATION_ACTIVE);
	}
	switch (ctx->mech.mechanism) {
		case CKM_RSA_PKCS:
		return (rsa_pkcs_sign(sess,	length_only,  ctx,
		    in_data,  in_data_len,
		    out_data, out_data_len));
		case CKM_MD5_RSA_PKCS:
		case CKM_SHA1_RSA_PKCS:
		return (rsa_hash_pkcs_sign(sess,	length_only, ctx,
		    in_data,  in_data_len,
		    out_data, out_data_len));

		case CKM_MD5_HMAC:
		case CKM_MD5_HMAC_GENERAL:
		return (md5_hmac_sign(sess,	length_only, ctx,
		    in_data,  in_data_len,
		    out_data, out_data_len));
		case CKM_SHA_1_HMAC:
		case CKM_SHA_1_HMAC_GENERAL:
		return (sha1_hmac_sign(sess,	length_only, ctx,
		    in_data,  in_data_len,
		    out_data, out_data_len));
		default:
			return (CKR_MECHANISM_INVALID);
	}
}

CK_RV
sign_mgr_sign_update(SESSION		* sess,
	SIGN_VERIFY_CONTEXT * ctx,
	CK_BYTE		* in_data,
	CK_ULONG		in_data_len)
{
	if (! sess || ! ctx || ! in_data) {
		return (CKR_FUNCTION_FAILED);
	}

	if (ctx->active == FALSE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}
	if (ctx->recover == TRUE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}
	ctx->multi = TRUE;

	switch (ctx->mech.mechanism) {
		case CKM_MD5_RSA_PKCS:
		case CKM_SHA1_RSA_PKCS:
			return (rsa_hash_pkcs_sign_update(sess, ctx,
			    in_data, in_data_len));
		default:
			return (CKR_MECHANISM_INVALID);
	}
}

CK_RV
sign_mgr_sign_final(SESSION		* sess,
	CK_BBOOL		length_only,
	SIGN_VERIFY_CONTEXT * ctx,
	CK_BYTE		* signature,
	CK_ULONG	    * sig_len)
{
	if (! sess || ! ctx) {
		return (CKR_FUNCTION_FAILED);
	}
	if (ctx->active == FALSE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}
	if (ctx->recover == TRUE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}
	switch (ctx->mech.mechanism) {
		case CKM_MD5_RSA_PKCS:
		case CKM_SHA1_RSA_PKCS:
			return (rsa_hash_pkcs_sign_final(sess, length_only,
			    ctx, signature, sig_len));
		default:
		return (CKR_MECHANISM_INVALID);
	}
}

CK_RV
sign_mgr_sign_recover(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)
{
	if (! sess || ! ctx) {
		return (CKR_FUNCTION_FAILED);
	}
	if (ctx->active == FALSE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}
	if (ctx->recover == FALSE) {
		return (CKR_OPERATION_NOT_INITIALIZED);
	}

	// if the caller just wants the signature length, there is no reason to
	// specify the input data.  I just need the input data length
	//
	if ((length_only == FALSE) && (! in_data || ! out_data)) {
		return (CKR_FUNCTION_FAILED);
	}
	if (ctx->multi == TRUE) {
		return (CKR_OPERATION_ACTIVE);
	}
	switch (ctx->mech.mechanism) {
		case CKM_RSA_PKCS:
			return (rsa_pkcs_sign(sess,	length_only,  ctx,
			    in_data,  in_data_len,
			    out_data, out_data_len));
		default:
			return (CKR_MECHANISM_INVALID);
	}
}