2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <pthread.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <sys/types.h>
2N/A#include <security/cryptoki.h>
2N/A#include "softSession.h"
2N/A#include "softObject.h"
2N/A#include "softCrypt.h"
2N/A#include <blowfish/blowfish_impl.h>
2N/A
2N/ACK_RV
2N/Asoft_blowfish_crypt_init_common(soft_session_t *session_p,
2N/A CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, soft_session_op_t op)
2N/A{
2N/A
2N/A size_t size;
2N/A soft_blowfish_ctx_t *soft_blowfish_ctx;
2N/A
2N/A soft_blowfish_ctx = calloc(1, sizeof (soft_blowfish_ctx_t));
2N/A if (soft_blowfish_ctx == NULL) {
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A
2N/A soft_blowfish_ctx->key_sched = blowfish_alloc_keysched(&size, 0);
2N/A if (soft_blowfish_ctx->key_sched == NULL) {
2N/A free(soft_blowfish_ctx);
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A
2N/A soft_blowfish_ctx->keysched_len = size;
2N/A
2N/A /*
2N/A * If this is a non-sensitive key and it does NOT have
2N/A * a key schedule yet, then allocate one and expand it.
2N/A * Otherwise, if it's a non-sensitive key, and it DOES have
2N/A * a key schedule already attached to it, just copy the
2N/A * pre-expanded schedule to the context and avoid the
2N/A * extra key schedule expansion operation.
2N/A */
2N/A if (!(key_p->bool_attr_mask & SENSITIVE_BOOL_ON)) {
2N/A if (OBJ_KEY_SCHED(key_p) == NULL) {
2N/A void *ks;
2N/A
2N/A (void) pthread_rwlock_wrlock(&key_p->object_rwlock);
2N/A if (OBJ_KEY_SCHED(key_p) == NULL) {
2N/A ks = blowfish_alloc_keysched(&size, 0);
2N/A if (ks == NULL) {
2N/A (void) pthread_rwlock_unlock(
2N/A &key_p->object_rwlock);
2N/A free(soft_blowfish_ctx->key_sched);
2N/A free(soft_blowfish_ctx);
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A
2N/A blowfish_init_keysched(OBJ_SEC_VALUE(key_p),
2N/A (OBJ_SEC_VALUE_LEN(key_p) * 8), ks);
2N/A
2N/A OBJ_KEY_SCHED_LEN(key_p) = size;
2N/A OBJ_KEY_SCHED(key_p) = ks;
2N/A }
2N/A (void) pthread_rwlock_unlock(&key_p->object_rwlock);
2N/A }
2N/A (void) memcpy(soft_blowfish_ctx->key_sched,
2N/A OBJ_KEY_SCHED(key_p), OBJ_KEY_SCHED_LEN(key_p));
2N/A soft_blowfish_ctx->keysched_len = OBJ_KEY_SCHED_LEN(key_p);
2N/A
2N/A } else {
2N/A /*
2N/A * Initialize key schedule for Blowfish.
2N/A * blowfish_init_keysched() requires key length in bits.
2N/A */
2N/A blowfish_init_keysched(OBJ_SEC_VALUE(key_p),
2N/A (OBJ_SEC_VALUE_LEN(key_p) * 8),
2N/A soft_blowfish_ctx->key_sched);
2N/A }
2N/A
2N/A soft_set_session_context(session_p, op, soft_blowfish_ctx, pMechanism);
2N/A
2N/A return (CKR_OK);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * soft_blowfish_encrypt_common()
2N/A *
2N/A * Arguments:
2N/A * session_p: pointer to soft_session_t struct
2N/A * pData: pointer to the input data to be encrypted
2N/A * ulDataLen: length of the input data
2N/A * pEncrypted: pointer to the output data after encryption
2N/A * pulEncryptedLen: pointer to the length of the output data
2N/A * update: boolean flag indicates caller is soft_encrypt
2N/A * or soft_encrypt_update
2N/A *
2N/A * Description:
2N/A * This function calls the corresponding encrypt routine based
2N/A * on the mechanism.
2N/A *
2N/A * Returns:
2N/A * CKR_OK: success
2N/A * CKR_BUFFER_TOO_SMALL: the output buffer provided by application
2N/A * is too small
2N/A * CKR_FUNCTION_FAILED: encrypt function failed
2N/A * CKR_DATA_LEN_RANGE: the input data is not a multiple of blocksize
2N/A */
2N/ACK_RV
2N/Asoft_blowfish_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData,
2N/A CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, CK_ULONG_PTR pulEncryptedLen,
2N/A boolean_t update)
2N/A{
2N/A
2N/A int rc = 0;
2N/A CK_RV rv = CKR_OK;
2N/A soft_blowfish_ctx_t *soft_blowfish_ctx =
2N/A (soft_blowfish_ctx_t *)session_p->encrypt.context;
2N/A blowfish_ctx_t *blowfish_ctx;
2N/A CK_BYTE *in_buf = NULL;
2N/A CK_BYTE *out_buf = NULL;
2N/A CK_ULONG out_len;
2N/A CK_ULONG total_len;
2N/A CK_ULONG remain;
2N/A crypto_data_t out;
2N/A
2N/A /*
2N/A * Blowfish only takes input length that is a multiple of blocksize
2N/A * for C_Encrypt function with the mechanism CKM_BLOWFISH_CBC.
2N/A *
2N/A */
2N/A if (!update) {
2N/A if ((ulDataLen % BLOWFISH_BLOCK_LEN) != 0) {
2N/A rv = CKR_DATA_LEN_RANGE;
2N/A goto cleanup;
2N/A }
2N/A
2N/A out_len = ulDataLen;
2N/A /*
2N/A * If application asks for the length of the output buffer
2N/A * to hold the ciphertext?
2N/A */
2N/A if (pEncrypted == NULL) {
2N/A *pulEncryptedLen = out_len;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A /* Is the application-supplied buffer large enough? */
2N/A if (*pulEncryptedLen < out_len) {
2N/A *pulEncryptedLen = out_len;
2N/A return (CKR_BUFFER_TOO_SMALL);
2N/A }
2N/A
2N/A in_buf = pData;
2N/A out_buf = pEncrypted;
2N/A } else {
2N/A /*
2N/A * Called by C_EncryptUpdate
2N/A *
2N/A * Add the lengths of last remaining data and current
2N/A * plaintext together to get the total input length.
2N/A */
2N/A total_len = soft_blowfish_ctx->remain_len + ulDataLen;
2N/A
2N/A /*
2N/A * If the total input length is less than one blocksize,
2N/A * we will need to delay encryption until when more data
2N/A * comes in next C_EncryptUpdate or when C_EncryptFinal
2N/A * is called.
2N/A */
2N/A if (total_len < BLOWFISH_BLOCK_LEN) {
2N/A if (pEncrypted != NULL) {
2N/A /*
2N/A * Save input data and its length in
2N/A * the remaining buffer of BLOWFISH context.
2N/A */
2N/A (void) memcpy(soft_blowfish_ctx->data +
2N/A soft_blowfish_ctx->remain_len, pData,
2N/A ulDataLen);
2N/A soft_blowfish_ctx->remain_len += ulDataLen;
2N/A }
2N/A
2N/A /* Set encrypted data length to 0. */
2N/A *pulEncryptedLen = 0;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A /* Compute the length of remaining data. */
2N/A remain = total_len % BLOWFISH_BLOCK_LEN;
2N/A
2N/A /*
2N/A * Make sure that the output length is a multiple of
2N/A * blocksize.
2N/A */
2N/A out_len = total_len - remain;
2N/A
2N/A /*
2N/A * If application asks for the length of the output buffer
2N/A * to hold the ciphertext?
2N/A */
2N/A if (pEncrypted == NULL) {
2N/A *pulEncryptedLen = out_len;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A /* Is the application-supplied buffer large enough? */
2N/A if (*pulEncryptedLen < out_len) {
2N/A *pulEncryptedLen = out_len;
2N/A return (CKR_BUFFER_TOO_SMALL);
2N/A }
2N/A
2N/A if (soft_blowfish_ctx->remain_len != 0) {
2N/A /*
2N/A * Copy last remaining data and current input data
2N/A * to the output buffer.
2N/A */
2N/A (void) memmove(pEncrypted +
2N/A soft_blowfish_ctx->remain_len,
2N/A pData, out_len - soft_blowfish_ctx->remain_len);
2N/A (void) memcpy(pEncrypted, soft_blowfish_ctx->data,
2N/A soft_blowfish_ctx->remain_len);
2N/A bzero(soft_blowfish_ctx->data,
2N/A soft_blowfish_ctx->remain_len);
2N/A
2N/A in_buf = pEncrypted;
2N/A } else {
2N/A in_buf = pData;
2N/A }
2N/A out_buf = pEncrypted;
2N/A }
2N/A
2N/A /*
2N/A * Begin Encryption now.
2N/A */
2N/A
2N/A out.cd_format = CRYPTO_DATA_RAW;
2N/A out.cd_offset = 0;
2N/A out.cd_length = out_len;
2N/A out.cd_raw.iov_base = (char *)out_buf;
2N/A out.cd_raw.iov_len = out_len;
2N/A
2N/A /* Encrypt multiple blocks of data. */
2N/A rc = blowfish_encrypt_contiguous_blocks(
2N/A (blowfish_ctx_t *)soft_blowfish_ctx->mode_ctx,
2N/A (char *)in_buf, out_len, &out);
2N/A
2N/A if (rc == 0) {
2N/A *pulEncryptedLen = out_len;
2N/A if (update) {
2N/A /*
2N/A * For encrypt update, if there is remaining data,
2N/A * save it and it's length in the context.
2N/A */
2N/A if (remain != 0)
2N/A (void) memcpy(soft_blowfish_ctx->data, pData +
2N/A (ulDataLen - remain), remain);
2N/A
2N/A soft_blowfish_ctx->remain_len = remain;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A } else {
2N/A *pulEncryptedLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A }
2N/A
2N/Acleanup:
2N/A (void) pthread_mutex_lock(&session_p->session_mutex);
2N/A blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->mode_ctx;
2N/A if (blowfish_ctx != NULL) {
2N/A bzero(blowfish_ctx->bc_keysched,
2N/A blowfish_ctx->bc_keysched_len);
2N/A free(soft_blowfish_ctx->mode_ctx);
2N/A }
2N/A
2N/A bzero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len);
2N/A free(soft_blowfish_ctx->key_sched);
2N/A (void) pthread_mutex_unlock(&session_p->session_mutex);
2N/A
2N/A soft_clear_session_context(session_p, SOFT_ENCRYPT_OP,
2N/A sizeof (soft_blowfish_ctx_t));
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A
2N/ACK_RV
2N/Asoft_blowfish_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted,
2N/A CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen,
2N/A boolean_t update)
2N/A{
2N/A
2N/A int rc = 0;
2N/A CK_RV rv = CKR_OK;
2N/A soft_blowfish_ctx_t *soft_blowfish_ctx =
2N/A (soft_blowfish_ctx_t *)session_p->decrypt.context;
2N/A blowfish_ctx_t *blowfish_ctx;
2N/A CK_BYTE *in_buf = NULL;
2N/A CK_BYTE *out_buf = NULL;
2N/A CK_ULONG out_len;
2N/A CK_ULONG total_len;
2N/A CK_ULONG remain;
2N/A crypto_data_t out;
2N/A
2N/A /*
2N/A * Blowfish only takes input length that is a multiple of 16 bytes
2N/A * for C_Decrypt function using CKM_BLOWFISH_CBC.
2N/A */
2N/A
2N/A if (!update) {
2N/A /* Called by C_Decrypt */
2N/A if ((ulEncryptedLen % BLOWFISH_BLOCK_LEN) != 0) {
2N/A rv = CKR_ENCRYPTED_DATA_LEN_RANGE;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * If application asks for the length of the output buffer
2N/A * to hold the plaintext?
2N/A */
2N/A if (pData == NULL) {
2N/A *pulDataLen = ulEncryptedLen;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A /* Is the application-supplied buffer large enough? */
2N/A if (*pulDataLen < ulEncryptedLen) {
2N/A *pulDataLen = ulEncryptedLen;
2N/A return (CKR_BUFFER_TOO_SMALL);
2N/A }
2N/A out_len = ulEncryptedLen;
2N/A in_buf = pEncrypted;
2N/A out_buf = pData;
2N/A } else {
2N/A /*
2N/A * Called by C_DecryptUpdate
2N/A *
2N/A * Add the lengths of last remaining data and current
2N/A * input data together to get the total input length.
2N/A */
2N/A total_len = soft_blowfish_ctx->remain_len + ulEncryptedLen;
2N/A
2N/A if (total_len < BLOWFISH_BLOCK_LEN) {
2N/A if (pData != NULL) {
2N/A (void) memcpy(soft_blowfish_ctx->data +
2N/A soft_blowfish_ctx->remain_len,
2N/A pEncrypted, ulEncryptedLen);
2N/A
2N/A soft_blowfish_ctx->remain_len += ulEncryptedLen;
2N/A }
2N/A
2N/A /* Set output data length to 0. */
2N/A *pulDataLen = 0;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A /* Compute the length of remaining data. */
2N/A remain = total_len % BLOWFISH_BLOCK_LEN;
2N/A
2N/A /*
2N/A * Make sure that the output length is a multiple of
2N/A * blocksize.
2N/A */
2N/A out_len = total_len - remain;
2N/A
2N/A /*
2N/A * if application asks for the length of the output buffer
2N/A * to hold the plaintext?
2N/A */
2N/A if (pData == NULL) {
2N/A *pulDataLen = out_len;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A /*
2N/A * Is the application-supplied buffer large enough?
2N/A */
2N/A if (*pulDataLen < out_len) {
2N/A *pulDataLen = out_len;
2N/A return (CKR_BUFFER_TOO_SMALL);
2N/A }
2N/A
2N/A if (soft_blowfish_ctx->remain_len != 0) {
2N/A /*
2N/A * Copy last remaining data and current input data
2N/A * to the output buffer.
2N/A */
2N/A (void) memmove(pData + soft_blowfish_ctx->remain_len,
2N/A pEncrypted,
2N/A out_len - soft_blowfish_ctx->remain_len);
2N/A (void) memcpy(pData, soft_blowfish_ctx->data,
2N/A soft_blowfish_ctx->remain_len);
2N/A bzero(soft_blowfish_ctx->data,
2N/A soft_blowfish_ctx->remain_len);
2N/A
2N/A
2N/A in_buf = pData;
2N/A } else {
2N/A in_buf = pEncrypted;
2N/A }
2N/A
2N/A out_buf = pData;
2N/A }
2N/A
2N/A out.cd_format = CRYPTO_DATA_RAW;
2N/A out.cd_offset = 0;
2N/A out.cd_length = out_len;
2N/A out.cd_raw.iov_base = (char *)out_buf;
2N/A out.cd_raw.iov_len = out_len;
2N/A
2N/A /* Decrypt multiple blocks of data. */
2N/A rc = blowfish_decrypt_contiguous_blocks(
2N/A (blowfish_ctx_t *)soft_blowfish_ctx->mode_ctx,
2N/A (char *)in_buf, out_len, &out);
2N/A
2N/A if (rc == 0) {
2N/A *pulDataLen = out_len;
2N/A if (update) {
2N/A /*
2N/A * For decrypt update, if there is remaining data,
2N/A * save it and its length in the context.
2N/A */
2N/A if (remain != 0)
2N/A (void) memcpy(soft_blowfish_ctx->data,
2N/A pEncrypted + (ulEncryptedLen - remain),
2N/A remain);
2N/A soft_blowfish_ctx->remain_len = remain;
2N/A return (CKR_OK);
2N/A }
2N/A
2N/A
2N/A } else {
2N/A *pulDataLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A }
2N/A
2N/Acleanup:
2N/A (void) pthread_mutex_lock(&session_p->session_mutex);
2N/A blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->mode_ctx;
2N/A if (blowfish_ctx != NULL) {
2N/A bzero(blowfish_ctx->bc_keysched,
2N/A blowfish_ctx->bc_keysched_len);
2N/A free(soft_blowfish_ctx->mode_ctx);
2N/A }
2N/A
2N/A bzero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len);
2N/A free(soft_blowfish_ctx->key_sched);
2N/A (void) pthread_mutex_unlock(&session_p->session_mutex);
2N/A
2N/A soft_clear_session_context(session_p, SOFT_DECRYPT_OP,
2N/A sizeof (soft_blowfish_ctx_t));
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Allocate and initialize a context for BLOWFISH CBC mode of operation.
2N/A */
2N/A
2N/Avoid *
2N/Ablowfish_cbc_ctx_init(void *key_sched, size_t size, uint8_t *ivec)
2N/A{
2N/A
2N/A cbc_ctx_t *cbc_ctx;
2N/A
2N/A if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A cbc_ctx->cbc_keysched = key_sched;
2N/A
2N/A (void) memcpy(&cbc_ctx->cbc_iv[0], ivec, BLOWFISH_BLOCK_LEN);
2N/A
2N/A cbc_ctx->cbc_lastp = (uint8_t *)&(cbc_ctx->cbc_iv);
2N/A cbc_ctx->cbc_keysched_len = size;
2N/A cbc_ctx->cbc_flags |= CBC_MODE;
2N/A
2N/A return (cbc_ctx);
2N/A}