/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <strings.h>
#include <md5.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/sha1.h>
#include <sys/sha2.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include "softGlobal.h"
#include "softOps.h"
#include "softSession.h"
#include "softObject.h"
/*
* soft_digest_init()
*
* Arguments:
* session_p: pointer to soft_session_t struct
* pMechanism: pointer to CK_MECHANISM struct provided by application
*
* Description:
* called by C_DigestInit(). This function allocates space for
* context, then calls the corresponding software provided digest
* init routine based on the mechanism.
*
* Returns:
* CKR_OK: success
* CKR_HOST_MEMORY: run out of system memory
* CKR_MECHANISM_INVALID: invalid mechanism type
*/
CK_RV
soft_digest_init(soft_session_t *session_p, CK_MECHANISM_PTR pMechanism)
{
switch (pMechanism->mechanism) {
case CKM_MD5:
(void) pthread_mutex_lock(&session_p->session_mutex);
session_p->digest.context = malloc(sizeof (MD5_CTX));
if (session_p->digest.context == NULL) {
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (CKR_HOST_MEMORY);
}
session_p->digest.mech.mechanism = CKM_MD5;
(void) pthread_mutex_unlock(&session_p->session_mutex);
MD5Init((MD5_CTX *)session_p->digest.context);
break;
case CKM_SHA_1:
(void) pthread_mutex_lock(&session_p->session_mutex);
session_p->digest.context = malloc(sizeof (SHA1_CTX));
if (session_p->digest.context == NULL) {
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (CKR_HOST_MEMORY);
}
session_p->digest.mech.mechanism = CKM_SHA_1;
session_p->digest.mech.pParameter = pMechanism->pParameter;
session_p->digest.mech.ulParameterLen =
pMechanism->ulParameterLen;
(void) pthread_mutex_unlock(&session_p->session_mutex);
SHA1Init((SHA1_CTX *)session_p->digest.context);
break;
case CKM_SHA256:
case CKM_SHA384:
case CKM_SHA512:
(void) pthread_mutex_lock(&session_p->session_mutex);
session_p->digest.context = malloc(sizeof (SHA2_CTX));
if (session_p->digest.context == NULL) {
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (CKR_HOST_MEMORY);
}
switch (pMechanism->mechanism) {
case CKM_SHA256:
session_p->digest.mech.mechanism = CKM_SHA256;
(void) pthread_mutex_unlock(&session_p->session_mutex);
SHA2Init(SHA256,
(SHA2_CTX *)session_p->digest.context);
break;
case CKM_SHA384:
session_p->digest.mech.mechanism = CKM_SHA384;
(void) pthread_mutex_unlock(&session_p->session_mutex);
SHA2Init(SHA384,
(SHA2_CTX *)session_p->digest.context);
break;
case CKM_SHA512:
session_p->digest.mech.mechanism = CKM_SHA512;
(void) pthread_mutex_unlock(&session_p->session_mutex);
SHA2Init(SHA512,
(SHA2_CTX *)session_p->digest.context);
break;
}
break;
default:
return (CKR_MECHANISM_INVALID);
}
return (CKR_OK);
}
/*
* soft_digest_common()
*
* Arguments:
* session_p: pointer to soft_session_t struct
* pData: pointer to the input data to be digested
* ulDataLen: length of the input data
* pDigest: pointer to the output data after digesting
* pulDigestLen: length of the output data
*
* Description:
* called by soft_digest() or soft_digest_final(). This function
* determines the length of output buffer and calls the corresponding
* software provided digest routine based on the mechanism.
*
* Returns:
* CKR_OK: success
* CKR_MECHANISM_INVALID: invalid mechanism type
* CKR_BUFFER_TOO_SMALL: the output buffer provided by application
* is too small
*/
CK_RV
soft_digest_common(soft_session_t *session_p, CK_BYTE_PTR pData,
CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen)
{
CK_ULONG digestLen = 0;
size_t len = 0;
/*
* Determine the output data length based on the mechanism
*/
switch (session_p->digest.mech.mechanism) {
case CKM_MD5:
digestLen = 16;
break;
case CKM_SHA_1:
digestLen = 20;
break;
case CKM_SHA256:
digestLen = 32;
break;
case CKM_SHA384:
digestLen = 48;
break;
case CKM_SHA512:
digestLen = 64;
break;
default:
return (CKR_MECHANISM_INVALID);
}
if (pDigest == NULL) {
/*
* Application only wants to know the length of the
* buffer needed to hold the message digest.
*/
*pulDigestLen = digestLen;
return (CKR_OK);
}
if (*pulDigestLen < digestLen) {
/*
* Application provides buffer too small to hold the
* digest message. Return the length of buffer needed
* to the application.
*/
*pulDigestLen = digestLen;
return (CKR_BUFFER_TOO_SMALL);
}
/*
* Call the corresponding system provided software digest routine.
* If the soft_digest_common() is called by soft_digest_final()
* the pData is NULL, and the ulDataLen is zero.
*/
switch (session_p->digest.mech.mechanism) {
case CKM_MD5:
if (pData != NULL) {
/*
* this is called by soft_digest()
*/
#ifdef __sparcv9
MD5Update((MD5_CTX *)session_p->digest.context,
/* LINTED */
pData, (uint_t)ulDataLen);
#else /* !__sparcv9 */
MD5Update((MD5_CTX *)session_p->digest.context,
pData, ulDataLen);
#endif /* __sparcv9 */
MD5Final(pDigest, (MD5_CTX *)session_p->digest.context);
} else {
/*
* this is called by soft_digest_final()
*/
MD5Final(pDigest, (MD5_CTX *)session_p->digest.context);
len = sizeof (MD5_CTX);
}
break;
case CKM_SHA_1:
if (pData != NULL) {
/*
* this is called by soft_digest()
*/
#ifdef __sparcv9
SHA1Update((SHA1_CTX *)session_p->digest.context,
/* LINTED */
pData, (uint32_t)ulDataLen);
#else /* !__sparcv9 */
SHA1Update((SHA1_CTX *)session_p->digest.context,
pData, ulDataLen);
#endif /* __sparcv9 */
SHA1Final(pDigest,
(SHA1_CTX *)session_p->digest.context);
} else {
/*
* this is called by soft_digest_final()
*/
SHA1Final(pDigest,
(SHA1_CTX *)session_p->digest.context);
len = sizeof (SHA1_CTX);
}
break;
case CKM_SHA256:
case CKM_SHA384:
case CKM_SHA512:
if (pData != NULL) {
/*
* this is called by soft_digest()
*/
SHA2Update((SHA2_CTX *)session_p->digest.context,
pData, ulDataLen);
SHA2Final(pDigest,
(SHA2_CTX *)session_p->digest.context);
} else {
/*
* this is called by soft_digest_final()
*/
SHA2Final(pDigest,
(SHA2_CTX *)session_p->digest.context);
len = sizeof (SHA2_CTX);
}
break;
}
/* Paranoia on behalf of C_DigestKey callers: bzero the context */
if (session_p->digest.flags & CRYPTO_KEY_DIGESTED) {
bzero(session_p->digest.context, len);
session_p->digest.flags &= ~CRYPTO_KEY_DIGESTED;
}
*pulDigestLen = digestLen;
(void) pthread_mutex_lock(&session_p->session_mutex);
free(session_p->digest.context);
session_p->digest.context = NULL;
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (CKR_OK);
}
/*
* soft_digest()
*
* Arguments:
* session_p: pointer to soft_session_t struct
* pData: pointer to the input data to be digested
* ulDataLen: length of the input data
* pDigest: pointer to the output data after digesting
* pulDigestLen: length of the output data
*
* Description:
* called by C_Digest(). This function calls soft_digest_common().
*
* Returns:
* see return values in soft_digest_common().
*/
CK_RV
soft_digest(soft_session_t *session_p, CK_BYTE_PTR pData, CK_ULONG ulDataLen,
CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen)
{
return (soft_digest_common(session_p, pData, ulDataLen,
pDigest, pulDigestLen));
}
/*
* soft_digest_update()
*
* Arguments:
* session_p: pointer to soft_session_t struct
* pPart: pointer to the input data to be digested
* ulPartLen: length of the input data
*
* Description:
* called by C_DigestUpdate(). This function calls the corresponding
* software provided digest update routine based on the mechanism.
*
* Returns:
* CKR_OK: success
* CKR_MECHANISM_INVALID: invalid MECHANISM type.
*/
CK_RV
soft_digest_update(soft_session_t *session_p, CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
switch (session_p->digest.mech.mechanism) {
case CKM_MD5:
#ifdef __sparcv9
MD5Update((MD5_CTX *)session_p->digest.context,
/* LINTED */
pPart, (uint_t)ulPartLen);
#else /* !__sparcv9 */
MD5Update((MD5_CTX *)session_p->digest.context,
pPart, ulPartLen);
#endif /* __sparcv9 */
break;
case CKM_SHA_1:
#ifdef __sparcv9
SHA1Update((SHA1_CTX *)session_p->digest.context,
/* LINTED */
pPart, (uint32_t)ulPartLen);
#else /* !__sparcv9 */
SHA1Update((SHA1_CTX *)session_p->digest.context,
pPart, ulPartLen);
#endif /* __sparcv9 */
break;
case CKM_SHA256:
case CKM_SHA384:
case CKM_SHA512:
SHA2Update((SHA2_CTX *)session_p->digest.context,
pPart, ulPartLen);
break;
default:
return (CKR_MECHANISM_INVALID);
}
return (CKR_OK);
}
/*
* soft_digest_final()
*
* Arguments:
* session_p: pointer to soft_session_t struct
* pDigest: pointer to the output data after digesting
* pulDigestLen: length of the output data
*
* Description:
* called by C_DigestFinal(). This function calls soft_digest_common().
*
* Returns:
* see return values in soft_digest_common().
*/
CK_RV
soft_digest_final(soft_session_t *session_p, CK_BYTE_PTR pDigest,
CK_ULONG_PTR pulDigestLen)
{
return (soft_digest_common(session_p, NULL, 0,
pDigest, pulDigestLen));
}
/*
* Perform digest init operation internally for the support of
* CKM_MD5_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_SHA1_KEY_DERIVATION
* and CKM_MD5_KEY_DERIVATION mechanisms.
*
* This function is called with the session being held, and without
* its mutex taken.
*/
CK_RV
soft_digest_init_internal(soft_session_t *session_p, CK_MECHANISM_PTR
pMechanism)
{
CK_RV rv;
(void) pthread_mutex_lock(&session_p->session_mutex);
/* Check to see if digest operation is already active */
if (session_p->digest.flags & CRYPTO_OPERATION_ACTIVE) {
(void) pthread_mutex_unlock(&session_p->session_mutex);
return (CKR_OPERATION_ACTIVE);
}
session_p->digest.flags = CRYPTO_OPERATION_ACTIVE;
(void) pthread_mutex_unlock(&session_p->session_mutex);
rv = soft_digest_init(session_p, pMechanism);
if (rv != CKR_OK) {
(void) pthread_mutex_lock(&session_p->session_mutex);
session_p->digest.flags &= ~CRYPTO_OPERATION_ACTIVE;
(void) pthread_mutex_unlock(&session_p->session_mutex);
}
return (rv);
}
/*
* Call soft_digest_update() function with the value of a secret key.
*/
CK_RV
soft_digest_key(soft_session_t *session_p, soft_object_t *key_p)
{
CK_RV rv;
/* Only secret key is allowed to be digested */
if (key_p->class != CKO_SECRET_KEY)
return (CKR_KEY_INDIGESTIBLE);
if ((OBJ_SEC_VALUE(key_p) == NULL) ||
(OBJ_SEC_VALUE_LEN(key_p) == 0))
return (CKR_KEY_SIZE_RANGE);
rv = soft_digest_update(session_p, OBJ_SEC_VALUE(key_p),
OBJ_SEC_VALUE_LEN(key_p));
return (rv);
}
/*
* This function releases allocated digest context. The caller
* may (lock_held == B_TRUE) or may not (lock_held == B_FALSE)
* hold a session mutex.
*/
void
soft_digest_cleanup(soft_session_t *session_p, boolean_t lock_held)
{
boolean_t lock_true = B_TRUE;
if (!lock_held)
(void) pthread_mutex_lock(&session_p->session_mutex);
if (session_p->digest.context != NULL) {
free(session_p->digest.context);
session_p->digest.context = NULL;
}
session_p->digest.flags = 0;
if (!lock_held)
SES_REFRELE(session_p, lock_true);
}