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) 2003, 2011, 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 <aes/aes_impl.h>
2N/A
2N/A#ifdef __amd64
2N/A#include <sys/auxv.h> /* getisax() */
2N/A#include <sys/auxv_386.h> /* AV_386_AES bit */
2N/A
2N/A/* Threshold to use optimized AES-NI Modes code when AES-NI is present */
2N/A#define AESNI_MODES_THRESHOLD 256 /* bytes */
2N/A
2N/A
2N/A/*
2N/A * Return 1 if executing on Intel with AES-NI instructions,
2N/A * otherwise 0 (i.e., Intel without AES-NI or AMD64).
2N/A * Cache the result, as the CPU can't change.
2N/A */
2N/Aint
2N/Asoft_intel_aes_instructions_present(void)
2N/A{
2N/A static int cached_result = -1;
2N/A
2N/A if (cached_result == -1) { /* first time */
2N/A uint_t ui = 0;
2N/A
2N/A (void) getisax(&ui, 1);
2N/A cached_result = (ui & AV_386_AES) != 0;
2N/A }
2N/A return (cached_result);
2N/A}
2N/A#endif /* __amd64 */
2N/A
2N/A
2N/A/*
2N/A * Allocate context for the active encryption or decryption operation, and
2N/A * generate AES key schedule to speed up the operation.
2N/A */
2N/ACK_RV
2N/Asoft_aes_crypt_init_common(soft_session_t *session_p,
2N/A CK_MECHANISM_PTR pMechanism, soft_object_t *key_p,
2N/A soft_session_op_t op)
2N/A{
2N/A size_t size;
2N/A soft_aes_ctx_t *soft_aes_ctx;
2N/A
2N/A soft_aes_ctx = calloc(1, sizeof (soft_aes_ctx_t));
2N/A if (soft_aes_ctx == NULL) {
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A
2N/A soft_aes_ctx->key_sched = aes_alloc_keysched(&size, 0);
2N/A if (soft_aes_ctx->key_sched == NULL) {
2N/A free(soft_aes_ctx);
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A
2N/A soft_aes_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 = aes_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_aes_ctx->key_sched);
2N/A free(soft_aes_ctx);
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A#ifdef __sparcv9
2N/A /* LINTED */
2N/A aes_init_keysched(OBJ_SEC_VALUE(key_p), (uint_t)
2N/A (OBJ_SEC_VALUE_LEN(key_p) * 8), ks);
2N/A#else /* !__sparcv9 */
2N/A aes_init_keysched(OBJ_SEC_VALUE(key_p),
2N/A (OBJ_SEC_VALUE_LEN(key_p) * 8), ks);
2N/A#endif /* __sparcv9 */
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_aes_ctx->key_sched, OBJ_KEY_SCHED(key_p),
2N/A OBJ_KEY_SCHED_LEN(key_p));
2N/A soft_aes_ctx->keysched_len = OBJ_KEY_SCHED_LEN(key_p);
2N/A } else {
2N/A /*
2N/A * Initialize key schedule for AES. aes_init_keysched()
2N/A * requires key length in bits.
2N/A */
2N/A#ifdef __sparcv9
2N/A /* LINTED */
2N/A aes_init_keysched(OBJ_SEC_VALUE(key_p), (uint_t)
2N/A (OBJ_SEC_VALUE_LEN(key_p) * 8), soft_aes_ctx->key_sched);
2N/A#else /* !__sparcv9 */
2N/A aes_init_keysched(OBJ_SEC_VALUE(key_p),
2N/A (OBJ_SEC_VALUE_LEN(key_p) * 8), soft_aes_ctx->key_sched);
2N/A#endif /* __sparcv9 */
2N/A }
2N/A
2N/A soft_set_session_context(session_p, op, soft_aes_ctx, pMechanism);
2N/A
2N/A return (CKR_OK);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * soft_aes_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_aes_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData,
2N/A CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted,
2N/A CK_ULONG_PTR pulEncryptedLen, boolean_t update)
2N/A{
2N/A
2N/A int rc = 0;
2N/A CK_RV rv = CKR_OK;
2N/A soft_aes_ctx_t *soft_aes_ctx =
2N/A (soft_aes_ctx_t *)session_p->encrypt.context;
2N/A aes_ctx_t *aes_ctx;
2N/A CK_MECHANISM_TYPE mechanism = session_p->encrypt.mech.mechanism;
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
2N/A if (mechanism == CKM_AES_CTR)
2N/A goto do_encryption;
2N/A
2N/A /*
2N/A * AES only takes input length that is a multiple of blocksize
2N/A * for C_Encrypt function with the mechanism CKM_AES_ECB or
2N/A * CKM_AES_CBC.
2N/A *
2N/A * AES allows any input length for C_Encrypt function with the
2N/A * mechanism CKM_AES_CBC_PAD and for C_EncryptUpdate function.
2N/A */
2N/A if ((!update) && (mechanism != CKM_AES_CBC_PAD)) {
2N/A if ((ulDataLen % AES_BLOCK_LEN) != 0) {
2N/A rv = CKR_DATA_LEN_RANGE;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A if (!update) {
2N/A /*
2N/A * Called by C_Encrypt
2N/A */
2N/A if (mechanism == CKM_AES_CBC_PAD) {
2N/A /*
2N/A * For CKM_AES_CBC_PAD, compute output length to
2N/A * count for the padding. If the length of input
2N/A * data is a multiple of blocksize, then make output
2N/A * length to be the sum of the input length and
2N/A * one blocksize. Otherwise, output length will
2N/A * be rounded up to the next multiple of blocksize.
2N/A */
2N/A out_len = AES_BLOCK_LEN *
2N/A (ulDataLen / AES_BLOCK_LEN + 1);
2N/A } else {
2N/A /*
2N/A * For non-padding mode, the output length will
2N/A * be same as the input length.
2N/A */
2N/A out_len = ulDataLen;
2N/A }
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 /* Encrypt pad bytes in a separate operation */
2N/A if (mechanism == CKM_AES_CBC_PAD) {
2N/A out_len -= AES_BLOCK_LEN;
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_aes_ctx->remain_len + ulDataLen;
2N/A
2N/A /*
2N/A * If the total input length is less than one blocksize,
2N/A * or if the total input length is just one blocksize and
2N/A * the mechanism is CKM_AES_CBC_PAD, we will need to delay
2N/A * encryption until when more data comes in next
2N/A * C_EncryptUpdate or when C_EncryptFinal is called.
2N/A */
2N/A if ((total_len < AES_BLOCK_LEN) ||
2N/A ((mechanism == CKM_AES_CBC_PAD) &&
2N/A (total_len == AES_BLOCK_LEN))) {
2N/A if (pEncrypted != NULL) {
2N/A /*
2N/A * Save input data and its length in
2N/A * the remaining buffer of AES context.
2N/A */
2N/A (void) memcpy(soft_aes_ctx->data +
2N/A soft_aes_ctx->remain_len, pData, ulDataLen);
2N/A soft_aes_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 % AES_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_aes_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 + soft_aes_ctx->remain_len,
2N/A pData, out_len - soft_aes_ctx->remain_len);
2N/A (void) memcpy(pEncrypted, soft_aes_ctx->data,
2N/A soft_aes_ctx->remain_len);
2N/A bzero(soft_aes_ctx->data, soft_aes_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/Ado_encryption:
2N/A /*
2N/A * Begin Encryption now.
2N/A */
2N/A switch (mechanism) {
2N/A
2N/A case CKM_AES_ECB:
2N/A {
2N/A#ifdef __amd64
2N/A if ((out_len < AESNI_MODES_THRESHOLD) ||
2N/A ! soft_intel_aes_instructions_present()) {
2N/A#else
2N/A /* LINTED E_CONSTANT_CONDITION */
2N/A if (0) {
2N/A#endif /* __amd64 */
2N/A /*
2N/A * AES-NI for small buffers -
2N/A * call block-level AES encrypt function directly.
2N/A */
2N/A ulong_t i;
2N/A uint8_t *tmp_inbuf;
2N/A uint8_t *tmp_outbuf;
2N/A
2N/A for (i = 0; i < out_len; i += AES_BLOCK_LEN) {
2N/A tmp_inbuf = &in_buf[i];
2N/A tmp_outbuf = &out_buf[i];
2N/A /* Crunch one block of data for AES. */
2N/A (void) aes_encrypt_block(
2N/A soft_aes_ctx->key_sched,
2N/A tmp_inbuf, tmp_outbuf);
2N/A }
2N/A
2N/A } else {
2N/A /*
2N/A * Use combined AES/ECB mode optimization in
2N/A * aes_encrypt_contiguous_blocks().
2N/A */
2N/A crypto_data_t out;
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
2N/A rc = aes_encrypt_contiguous_blocks(
2N/A (aes_ctx_t *)soft_aes_ctx->mode_ctx,
2N/A (char *)in_buf, out_len, &out);
2N/A
2N/A if (rc != 0) {
2N/A *pulEncryptedLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A if (update) {
2N/A /*
2N/A * For encrypt update, if there is remaining
2N/A * data, save it and its length in the context.
2N/A */
2N/A if (remain != 0)
2N/A (void) memcpy(soft_aes_ctx->data, pData +
2N/A (ulDataLen - remain), remain);
2N/A soft_aes_ctx->remain_len = remain;
2N/A }
2N/A
2N/A *pulEncryptedLen = out_len;
2N/A break;
2N/A }
2N/A
2N/A case CKM_AES_CBC:
2N/A case CKM_AES_CBC_PAD:
2N/A {
2N/A crypto_data_t out;
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 = aes_encrypt_contiguous_blocks(
2N/A (aes_ctx_t *)soft_aes_ctx->mode_ctx,
2N/A (char *)in_buf, out_len, &out);
2N/A
2N/A if (rc != 0) {
2N/A *pulEncryptedLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (update) {
2N/A /*
2N/A * For encrypt 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_aes_ctx->data, pData +
2N/A (ulDataLen - remain), remain);
2N/A soft_aes_ctx->remain_len = remain;
2N/A
2N/A } else if (mechanism == CKM_AES_CBC_PAD) {
2N/A /*
2N/A * Save the remainder of the input
2N/A * block in a temporary block because
2N/A * we don't want to overrun the buffer
2N/A * by tacking on pad bytes.
2N/A */
2N/A CK_BYTE tmpblock[AES_BLOCK_LEN];
2N/A (void) memcpy(tmpblock, in_buf + out_len,
2N/A ulDataLen - out_len);
2N/A soft_add_pkcs7_padding(tmpblock +
2N/A (ulDataLen - out_len),
2N/A AES_BLOCK_LEN, ulDataLen - out_len);
2N/A
2N/A out.cd_offset = out_len;
2N/A out.cd_length = AES_BLOCK_LEN;
2N/A out.cd_raw.iov_base = (char *)out_buf;
2N/A out.cd_raw.iov_len = out_len + AES_BLOCK_LEN;
2N/A
2N/A /* Encrypt last block containing pad bytes. */
2N/A rc = aes_encrypt_contiguous_blocks(
2N/A (aes_ctx_t *)soft_aes_ctx->mode_ctx,
2N/A (char *)tmpblock, AES_BLOCK_LEN, &out);
2N/A
2N/A out_len += AES_BLOCK_LEN;
2N/A }
2N/A
2N/A if (rc != 0) {
2N/A *pulEncryptedLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A
2N/A *pulEncryptedLen = out_len;
2N/A break;
2N/A }
2N/A
2N/A case CKM_AES_CTR:
2N/A {
2N/A crypto_data_t out;
2N/A
2N/A out.cd_format = CRYPTO_DATA_RAW;
2N/A out.cd_offset = 0;
2N/A out.cd_length = *pulEncryptedLen;
2N/A out.cd_raw.iov_base = (char *)pEncrypted;
2N/A out.cd_raw.iov_len = *pulEncryptedLen;
2N/A
2N/A rc = aes_encrypt_contiguous_blocks(soft_aes_ctx->mode_ctx,
2N/A (char *)pData, ulDataLen, &out);
2N/A
2N/A if (rc != 0) {
2N/A *pulEncryptedLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A *pulEncryptedLen = ulDataLen;
2N/A }
2N/A } /* end switch */
2N/A
2N/A if (update)
2N/A return (CKR_OK);
2N/A
2N/A /*
2N/A * The following code will be executed if the caller is
2N/A * soft_encrypt() or an error occurred. The encryption
2N/A * operation will be terminated so we need to do some cleanup.
2N/A */
2N/Acleanup:
2N/A (void) pthread_mutex_lock(&session_p->session_mutex);
2N/A aes_ctx = (aes_ctx_t *)soft_aes_ctx->mode_ctx;
2N/A if (aes_ctx != NULL) {
2N/A bzero(aes_ctx->ac_keysched, aes_ctx->ac_keysched_len);
2N/A free(soft_aes_ctx->mode_ctx);
2N/A }
2N/A
2N/A bzero(soft_aes_ctx->key_sched, soft_aes_ctx->keysched_len);
2N/A free(soft_aes_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_aes_ctx_t));
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * soft_aes_decrypt_common()
2N/A *
2N/A * Arguments:
2N/A * session_p: pointer to soft_session_t struct
2N/A * pEncrypted: pointer to the input data to be decrypted
2N/A * ulEncryptedLen: length of the input data
2N/A * pData: pointer to the output data
2N/A * pulDataLen: pointer to the length of the output data
2N/A * Update: boolean flag indicates caller is soft_decrypt
2N/A * or soft_decrypt_update
2N/A *
2N/A * Description:
2N/A * This function calls the corresponding decrypt 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_ENCRYPTED_DATA_LEN_RANGE: the input data is not a multiple
2N/A * of blocksize
2N/A * CKR_FUNCTION_FAILED: decrypt function failed
2N/A */
2N/ACK_RV
2N/Asoft_aes_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted,
2N/A CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData,
2N/A CK_ULONG_PTR pulDataLen, boolean_t update)
2N/A{
2N/A
2N/A int rc = 0;
2N/A CK_RV rv = CKR_OK;
2N/A soft_aes_ctx_t *soft_aes_ctx =
2N/A (soft_aes_ctx_t *)session_p->decrypt.context;
2N/A aes_ctx_t *aes_ctx;
2N/A CK_MECHANISM_TYPE mechanism = session_p->decrypt.mech.mechanism;
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
2N/A if (mechanism == CKM_AES_CTR)
2N/A goto do_decryption;
2N/A
2N/A /*
2N/A * AES only takes input length that is a multiple of 16 bytes
2N/A * for C_Decrypt function with the mechanism CKM_AES_ECB,
2N/A * CKM_AES_CBC or CKM_AES_CBC_PAD.
2N/A *
2N/A * AES allows any input length for C_DecryptUpdate function.
2N/A */
2N/A if (!update) {
2N/A /*
2N/A * Called by C_Decrypt
2N/A */
2N/A if ((ulEncryptedLen % AES_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 (mechanism != CKM_AES_CBC_PAD) {
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 } else {
2N/A /*
2N/A * For CKM_AES_CBC_PAD, we don't know how
2N/A * many bytes for padding at this time, so
2N/A * we'd assume one block was padded.
2N/A */
2N/A if (*pulDataLen < (ulEncryptedLen - AES_BLOCK_LEN)) {
2N/A *pulDataLen = ulEncryptedLen - AES_BLOCK_LEN;
2N/A return (CKR_BUFFER_TOO_SMALL);
2N/A }
2N/A out_len = ulEncryptedLen - AES_BLOCK_LEN;
2N/A }
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_aes_ctx->remain_len + ulEncryptedLen;
2N/A
2N/A /*
2N/A * If the total input length is less than one blocksize,
2N/A * or if the total input length is just one blocksize and
2N/A * the mechanism is CKM_AES_CBC_PAD, we will need to delay
2N/A * decryption until when more data comes in next
2N/A * C_DecryptUpdate or when C_DecryptFinal is called.
2N/A */
2N/A if ((total_len < AES_BLOCK_LEN) ||
2N/A ((mechanism == CKM_AES_CBC_PAD) &&
2N/A (total_len == AES_BLOCK_LEN))) {
2N/A if (pData != NULL) {
2N/A /*
2N/A * Save input data and its length in
2N/A * the remaining buffer of AES context.
2N/A */
2N/A (void) memcpy(soft_aes_ctx->data +
2N/A soft_aes_ctx->remain_len,
2N/A pEncrypted, ulEncryptedLen);
2N/A soft_aes_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 % AES_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 if (mechanism == CKM_AES_CBC_PAD) {
2N/A /*
2N/A * If the input data length is a multiple of
2N/A * blocksize, then save the last block of input
2N/A * data in the remaining buffer. C_DecryptFinal
2N/A * will handle this last block of data.
2N/A */
2N/A if (remain == 0) {
2N/A remain = AES_BLOCK_LEN;
2N/A out_len -= AES_BLOCK_LEN;
2N/A }
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 = 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_aes_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_aes_ctx->remain_len,
2N/A pEncrypted, out_len - soft_aes_ctx->remain_len);
2N/A (void) memcpy(pData, soft_aes_ctx->data,
2N/A soft_aes_ctx->remain_len);
2N/A bzero(soft_aes_ctx->data, soft_aes_ctx->remain_len);
2N/A
2N/A in_buf = pData;
2N/A } else {
2N/A in_buf = pEncrypted;
2N/A }
2N/A out_buf = pData;
2N/A }
2N/A
2N/Ado_decryption:
2N/A /*
2N/A * Begin Decryption.
2N/A */
2N/A switch (mechanism) {
2N/A
2N/A case CKM_AES_ECB:
2N/A {
2N/A#ifdef __amd64
2N/A if ((out_len < AESNI_MODES_THRESHOLD) ||
2N/A ! soft_intel_aes_instructions_present()) {
2N/A#else
2N/A /* LINTED E_CONSTANT_CONDITION */
2N/A if (0) {
2N/A#endif
2N/A /*
2N/A * AES-NI for small buffers -
2N/A * call block-level AES encrypt function directly.
2N/A */
2N/A ulong_t i;
2N/A uint8_t *tmp_inbuf;
2N/A uint8_t *tmp_outbuf;
2N/A
2N/A for (i = 0; i < out_len; i += AES_BLOCK_LEN) {
2N/A tmp_inbuf = &in_buf[i];
2N/A tmp_outbuf = &out_buf[i];
2N/A /* Crunch one block of data for AES. */
2N/A (void) aes_decrypt_block(
2N/A soft_aes_ctx->key_sched,
2N/A tmp_inbuf, tmp_outbuf);
2N/A }
2N/A
2N/A } else {
2N/A /*
2N/A * Use combined AES/ECB mode optimization in
2N/A * aes_decrypt_contiguous_blocks().
2N/A */
2N/A crypto_data_t out;
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
2N/A rc = aes_decrypt_contiguous_blocks(
2N/A (aes_ctx_t *)soft_aes_ctx->mode_ctx,
2N/A (char *)in_buf, out_len, &out);
2N/A
2N/A if (rc != 0) {
2N/A *pulDataLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A if (update) {
2N/A /*
2N/A * For decrypt update, if there is remaining
2N/A * data, save it and its length in the context.
2N/A */
2N/A if (remain != 0)
2N/A (void) memcpy(soft_aes_ctx->data, pEncrypted +
2N/A (ulEncryptedLen - remain), remain);
2N/A soft_aes_ctx->remain_len = remain;
2N/A }
2N/A
2N/A *pulDataLen = out_len;
2N/A
2N/A break;
2N/A }
2N/A
2N/A case CKM_AES_CBC:
2N/A case CKM_AES_CBC_PAD:
2N/A {
2N/A crypto_data_t out;
2N/A CK_ULONG rem_len;
2N/A uint8_t last_block[AES_BLOCK_LEN];
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 = aes_decrypt_contiguous_blocks(
2N/A (aes_ctx_t *)soft_aes_ctx->mode_ctx,
2N/A (char *)in_buf, out_len, &out);
2N/A
2N/A if (rc != 0) {
2N/A *pulDataLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((mechanism == CKM_AES_CBC_PAD) && (!update)) {
2N/A /* Decrypt last block containing pad bytes. */
2N/A out.cd_offset = 0;
2N/A out.cd_length = AES_BLOCK_LEN;
2N/A out.cd_raw.iov_base = (char *)last_block;
2N/A out.cd_raw.iov_len = AES_BLOCK_LEN;
2N/A
2N/A /* Decrypt last block containing pad bytes. */
2N/A rc = aes_decrypt_contiguous_blocks(
2N/A (aes_ctx_t *)soft_aes_ctx->mode_ctx,
2N/A (char *)in_buf + out_len, AES_BLOCK_LEN, &out);
2N/A
2N/A if (rc != 0) {
2N/A *pulDataLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * Remove padding bytes after decryption of
2N/A * ciphertext block to produce the original
2N/A * plaintext.
2N/A */
2N/A rv = soft_remove_pkcs7_padding(last_block,
2N/A AES_BLOCK_LEN, &rem_len);
2N/A if (rv == CKR_OK) {
2N/A if (rem_len != 0)
2N/A (void) memcpy(out_buf + out_len,
2N/A last_block, rem_len);
2N/A *pulDataLen = out_len + rem_len;
2N/A } else {
2N/A *pulDataLen = 0;
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A *pulDataLen = out_len;
2N/A }
2N/A
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_aes_ctx->data, pEncrypted +
2N/A (ulEncryptedLen - remain), remain);
2N/A soft_aes_ctx->remain_len = remain;
2N/A }
2N/A
2N/A if (rc != 0) {
2N/A *pulDataLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A
2N/A break;
2N/A }
2N/A
2N/A case CKM_AES_CTR:
2N/A {
2N/A crypto_data_t out;
2N/A
2N/A out.cd_format = CRYPTO_DATA_RAW;
2N/A out.cd_offset = 0;
2N/A out.cd_length = *pulDataLen;
2N/A out.cd_raw.iov_base = (char *)pData;
2N/A out.cd_raw.iov_len = *pulDataLen;
2N/A
2N/A rc = aes_decrypt_contiguous_blocks(soft_aes_ctx->mode_ctx,
2N/A (char *)pEncrypted, ulEncryptedLen, &out);
2N/A
2N/A if (rc != 0) {
2N/A *pulDataLen = 0;
2N/A rv = CKR_FUNCTION_FAILED;
2N/A goto cleanup;
2N/A }
2N/A *pulDataLen = ulEncryptedLen;
2N/A }
2N/A } /* end switch */
2N/A
2N/A if (update)
2N/A return (CKR_OK);
2N/A
2N/A /*
2N/A * The following code will be executed if the caller is
2N/A * soft_decrypt() or an error occurred. The decryption
2N/A * operation will be terminated so we need to do some cleanup.
2N/A */
2N/Acleanup:
2N/A (void) pthread_mutex_lock(&session_p->session_mutex);
2N/A aes_ctx = (aes_ctx_t *)soft_aes_ctx->mode_ctx;
2N/A if (aes_ctx != NULL) {
2N/A bzero(aes_ctx->ac_keysched, aes_ctx->ac_keysched_len);
2N/A free(soft_aes_ctx->mode_ctx);
2N/A }
2N/A
2N/A bzero(soft_aes_ctx->key_sched, soft_aes_ctx->keysched_len);
2N/A free(soft_aes_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_aes_ctx_t));
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Allocate and initialize a context for AES ECB mode of operation.
2N/A * This is done only for AMD64 to use combined AES/ECB mode
2N/A * optimization. Otherwise, the block-level AES encrypt and decrypt
2N/A * functions are called directly.
2N/A */
2N/Avoid *
2N/Aaes_ecb_ctx_init(void *key_sched, size_t size)
2N/A{
2N/A
2N/A ecb_ctx_t *ecb_ctx;
2N/A
2N/A if ((ecb_ctx = calloc(1, sizeof (ecb_ctx_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A ecb_ctx->ecb_keysched = key_sched;
2N/A ecb_ctx->ecb_keysched_len = size;
2N/A ecb_ctx->ecb_flags |= ECB_MODE;
2N/A
2N/A return (ecb_ctx);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Allocate and initialize a context for AES CBC mode of operation.
2N/A */
2N/Avoid *
2N/Aaes_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 cbc_ctx->cbc_keysched_len = size;
2N/A
2N/A (void) memcpy(&cbc_ctx->cbc_iv[0], ivec, AES_BLOCK_LEN);
2N/A
2N/A cbc_ctx->cbc_lastp = (uint8_t *)cbc_ctx->cbc_iv;
2N/A cbc_ctx->cbc_flags |= CBC_MODE;
2N/A
2N/A return (cbc_ctx);
2N/A}
2N/A
2N/A/*
2N/A * Allocate and initialize a context for AES CTR mode of operation.
2N/A */
2N/Avoid *
2N/Aaes_ctr_ctx_init(void *key_sched, size_t size, uint8_t *param)
2N/A{
2N/A
2N/A ctr_ctx_t *ctr_ctx;
2N/A CK_AES_CTR_PARAMS *pp;
2N/A
2N/A /* LINTED: pointer alignment */
2N/A pp = (CK_AES_CTR_PARAMS *)param;
2N/A
2N/A if ((ctr_ctx = calloc(1, sizeof (ctr_ctx_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A ctr_ctx->ctr_keysched = key_sched;
2N/A ctr_ctx->ctr_keysched_len = size;
2N/A
2N/A if (ctr_init_ctx(ctr_ctx, pp->ulCounterBits, pp->cb, aes_copy_block)
2N/A != CRYPTO_SUCCESS) {
2N/A free(ctr_ctx);
2N/A return (NULL);
2N/A }
2N/A ctr_ctx->ctr_flags |= CTR_MODE;
2N/A
2N/A return (ctr_ctx);
2N/A}