1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <pthread.h>
  27 #include <stdlib.h>
  28 #include <string.h>
  29 #include <strings.h>
  30 #include <sys/types.h>
  31 #include <security/cryptoki.h>
  32 #include "softSession.h"
  33 #include "softObject.h"
  34 #include "softCrypt.h"
  35 #include <blowfish_impl.h>
  36 
  37 CK_RV
  38 soft_blowfish_crypt_init_common(soft_session_t *session_p,
  39     CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t encrypt) {
  40 
  41         size_t size;
  42         soft_blowfish_ctx_t *soft_blowfish_ctx;
  43 
  44         soft_blowfish_ctx = calloc(1, sizeof (soft_blowfish_ctx_t));
  45         if (soft_blowfish_ctx == NULL) {
  46                 return (CKR_HOST_MEMORY);
  47         }
  48 
  49         soft_blowfish_ctx->key_sched = blowfish_alloc_keysched(&size, 0);
  50 
  51         if (soft_blowfish_ctx->key_sched == NULL) {
  52                 free(soft_blowfish_ctx);
  53                 return (CKR_HOST_MEMORY);
  54         }
  55 
  56         soft_blowfish_ctx->keysched_len = size;
  57 
  58         (void) pthread_mutex_lock(&session_p->session_mutex);
  59         if (encrypt) {
  60                 /* Called by C_EncryptInit */
  61                 session_p->encrypt.context = soft_blowfish_ctx;
  62                 session_p->encrypt.mech.mechanism = pMechanism->mechanism;
  63         } else {
  64                 /* Called by C_DecryptInit */
  65                 session_p->decrypt.context = soft_blowfish_ctx;
  66                 session_p->decrypt.mech.mechanism = pMechanism->mechanism;
  67         }
  68         (void) pthread_mutex_unlock(&session_p->session_mutex);
  69 
  70         /*
  71          * If this is a non-sensitive key and it does NOT have
  72          * a key schedule yet, then allocate one and expand it.
  73          * Otherwise, if it's a non-sensitive key, and it DOES have
  74          * a key schedule already attached to it, just copy the
  75          * pre-expanded schedule to the context and avoid the
  76          * extra key schedule expansion operation.
  77          */
  78         if (!(key_p->bool_attr_mask & SENSITIVE_BOOL_ON)) {
  79                 if (OBJ_KEY_SCHED(key_p) == NULL) {
  80                         void *ks;
  81 
  82                         (void) pthread_mutex_lock(&key_p->object_mutex);
  83                         if (OBJ_KEY_SCHED(key_p) == NULL) {
  84                                 ks = blowfish_alloc_keysched(&size, 0);
  85                                 if (ks == NULL) {
  86                                         (void) pthread_mutex_unlock(
  87                                             &key_p->object_mutex);
  88                                         free(soft_blowfish_ctx);
  89                                         return (CKR_HOST_MEMORY);
  90                                 }
  91 
  92                                 blowfish_init_keysched(OBJ_SEC_VALUE(key_p),
  93                                     (OBJ_SEC_VALUE_LEN(key_p) * 8), ks);
  94 
  95                                 OBJ_KEY_SCHED_LEN(key_p) = size;
  96                                 OBJ_KEY_SCHED(key_p) = ks;
  97                         }
  98                         (void) pthread_mutex_unlock(&key_p->object_mutex);
  99                 }
 100                 (void) memcpy(soft_blowfish_ctx->key_sched,
 101                     OBJ_KEY_SCHED(key_p), OBJ_KEY_SCHED_LEN(key_p));
 102                 soft_blowfish_ctx->keysched_len = OBJ_KEY_SCHED_LEN(key_p);
 103 
 104         } else {
 105                 /*
 106                  * Initialize key schedule for Blowfish.
 107                  * blowfish_init_keysched() requires key length in bits.
 108                  */
 109                 blowfish_init_keysched(OBJ_SEC_VALUE(key_p),
 110                     (OBJ_SEC_VALUE_LEN(key_p) * 8),
 111                     soft_blowfish_ctx->key_sched);
 112         }
 113         return (CKR_OK);
 114 }
 115 
 116 
 117 /*
 118  * soft_blowfish_encrypt_common()
 119  *
 120  * Arguments:
 121  *      session_p:      pointer to soft_session_t struct
 122  *      pData:          pointer to the input data to be encrypted
 123  *      ulDataLen:      length of the input data
 124  *      pEncrypted:     pointer to the output data after encryption
 125  *      pulEncryptedLen: pointer to the length of the output data
 126  *      update:         boolean flag indicates caller is soft_encrypt
 127  *                      or soft_encrypt_update
 128  *
 129  * Description:
 130  *      This function calls the corresponding encrypt routine based
 131  *      on the mechanism.
 132  *
 133  * Returns:
 134  *      CKR_OK: success
 135  *      CKR_BUFFER_TOO_SMALL: the output buffer provided by application
 136  *                            is too small
 137  *      CKR_FUNCTION_FAILED: encrypt function failed
 138  *      CKR_DATA_LEN_RANGE: the input data is not a multiple of blocksize
 139  */
 140 CK_RV
 141 soft_blowfish_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData,
 142     CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, CK_ULONG_PTR pulEncryptedLen,
 143     boolean_t update) {
 144 
 145         int rc = 0;
 146         CK_RV rv = CKR_OK;
 147         soft_blowfish_ctx_t *soft_blowfish_ctx =
 148             (soft_blowfish_ctx_t *)session_p->encrypt.context;
 149         blowfish_ctx_t *blowfish_ctx;
 150         CK_BYTE *in_buf = NULL;
 151         CK_BYTE *out_buf = NULL;
 152         CK_ULONG out_len;
 153         CK_ULONG total_len;
 154         CK_ULONG remain;
 155         crypto_data_t out;
 156 
 157         /*
 158          * Blowfish only takes input length that is a multiple of blocksize
 159          * for C_Encrypt function with the mechanism CKM_BLOWFISH_CBC.
 160          *
 161          */
 162         if (!update) {
 163                 if ((ulDataLen % BLOWFISH_BLOCK_LEN) != 0) {
 164                         rv = CKR_DATA_LEN_RANGE;
 165                         goto cleanup;
 166                 }
 167 
 168                 out_len = ulDataLen;
 169                 /*
 170                  * If application asks for the length of the output buffer
 171                  * to hold the ciphertext?
 172                  */
 173                 if (pEncrypted == NULL) {
 174                         *pulEncryptedLen = out_len;
 175                         return (CKR_OK);
 176                 }
 177 
 178                 /* Is the application-supplied buffer large enough? */
 179                 if (*pulEncryptedLen < out_len) {
 180                         *pulEncryptedLen = out_len;
 181                         return (CKR_BUFFER_TOO_SMALL);
 182                 }
 183 
 184                 in_buf = pData;
 185                 out_buf = pEncrypted;
 186         } else {
 187                 /*
 188                  * Called by C_EncryptUpdate
 189                  *
 190                  * Add the lengths of last remaining data and current
 191                  * plaintext together to get the total input length.
 192                  */
 193                 total_len = soft_blowfish_ctx->remain_len + ulDataLen;
 194 
 195                 /*
 196                  * If the total input length is less than one blocksize,
 197                  * we will need to delay encryption until when more data
 198                  * comes in next C_EncryptUpdate or when C_EncryptFinal
 199                  * is called.
 200                  */
 201                 if (total_len < BLOWFISH_BLOCK_LEN) {
 202                         if (pEncrypted != NULL) {
 203                                 /*
 204                                  * Save input data and its length in
 205                                  * the remaining buffer of BLOWFISH context.
 206                                  */
 207                                 (void) memcpy(soft_blowfish_ctx->data +
 208                                     soft_blowfish_ctx->remain_len, pData,
 209                                     ulDataLen);
 210                                 soft_blowfish_ctx->remain_len += ulDataLen;
 211                         }
 212 
 213                         /* Set encrypted data length to 0. */
 214                         *pulEncryptedLen = 0;
 215                         return (CKR_OK);
 216                 }
 217 
 218                 /* Compute the length of remaing data. */
 219                 remain = total_len % BLOWFISH_BLOCK_LEN;
 220 
 221                 /*
 222                  * Make sure that the output length is a multiple of
 223                  * blocksize.
 224                  */
 225                 out_len = total_len - remain;
 226 
 227                 /*
 228                  * If application asks for the length of the output buffer
 229                  * to hold the ciphertext?
 230                  */
 231                 if (pEncrypted == NULL) {
 232                         *pulEncryptedLen = out_len;
 233                         return (CKR_OK);
 234                 }
 235 
 236                 /* Is the application-supplied buffer large enough? */
 237                 if (*pulEncryptedLen < out_len) {
 238                         *pulEncryptedLen = out_len;
 239                         return (CKR_BUFFER_TOO_SMALL);
 240                 }
 241 
 242                 if (soft_blowfish_ctx->remain_len != 0) {
 243                         /*
 244                          * Copy last remaining data and current input data
 245                          * to the output buffer.
 246                          */
 247                         (void) memmove(pEncrypted +
 248                             soft_blowfish_ctx->remain_len,
 249                             pData, out_len - soft_blowfish_ctx->remain_len);
 250                         (void) memcpy(pEncrypted, soft_blowfish_ctx->data,
 251                             soft_blowfish_ctx->remain_len);
 252                         bzero(soft_blowfish_ctx->data,
 253                             soft_blowfish_ctx->remain_len);
 254 
 255                         in_buf = pEncrypted;
 256                 } else {
 257                         in_buf = pData;
 258                 }
 259                 out_buf = pEncrypted;
 260         }
 261 
 262         /*
 263          * Begin Encryption now.
 264          */
 265 
 266         out.cd_format = CRYPTO_DATA_RAW;
 267         out.cd_offset = 0;
 268         out.cd_length = out_len;
 269         out.cd_raw.iov_base = (char *)out_buf;
 270         out.cd_raw.iov_len = out_len;
 271 
 272         /* Encrypt multiple blocks of data. */
 273         rc = blowfish_encrypt_contiguous_blocks(
 274                 (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc,
 275                     (char *)in_buf, out_len, &out);
 276 
 277         if (rc == 0) {
 278                 *pulEncryptedLen = out_len;
 279                 if (update) {
 280                         /*
 281                          * For encrypt update, if there is remaining data,
 282                          * save it and it's length in the context.
 283                          */
 284                         if (remain != 0)
 285                                 (void) memcpy(soft_blowfish_ctx->data, pData +
 286                                     (ulDataLen - remain), remain);
 287 
 288                         soft_blowfish_ctx->remain_len = remain;
 289                         return (CKR_OK);
 290                 }
 291 
 292         } else {
 293                 *pulEncryptedLen = 0;
 294                 rv = CKR_FUNCTION_FAILED;
 295         }
 296 
 297 cleanup:
 298         (void) pthread_mutex_lock(&session_p->session_mutex);
 299         blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc;
 300         if (blowfish_ctx != NULL) {
 301                 bzero(blowfish_ctx->bc_keysched,
 302                     blowfish_ctx->bc_keysched_len);
 303                 free(soft_blowfish_ctx->blowfish_cbc);
 304         }
 305 
 306         bzero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len);
 307         free(soft_blowfish_ctx->key_sched);
 308         free(session_p->encrypt.context);
 309         session_p->encrypt.context = NULL;
 310         (void) pthread_mutex_unlock(&session_p->session_mutex);
 311 
 312         return (rv);
 313 }
 314 
 315 
 316 CK_RV
 317 soft_blowfish_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted,
 318     CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen,
 319     boolean_t update) {
 320 
 321         int rc = 0;
 322         CK_RV rv = CKR_OK;
 323         soft_blowfish_ctx_t *soft_blowfish_ctx =
 324             (soft_blowfish_ctx_t *)session_p->decrypt.context;
 325         blowfish_ctx_t *blowfish_ctx;
 326         CK_BYTE *in_buf = NULL;
 327         CK_BYTE *out_buf = NULL;
 328         CK_ULONG out_len;
 329         CK_ULONG total_len;
 330         CK_ULONG remain;
 331         crypto_data_t out;
 332 
 333         /*
 334          * Blowfish only takes input length that is a multiple of 16 bytes
 335          * for C_Decrypt function using CKM_BLOWFISH_CBC.
 336          */
 337 
 338         if (!update) {
 339                 /* Called by C_Decrypt */
 340                 if ((ulEncryptedLen % BLOWFISH_BLOCK_LEN) != 0) {
 341                         rv = CKR_ENCRYPTED_DATA_LEN_RANGE;
 342                         goto cleanup;
 343                 }
 344 
 345                 /*
 346                  * If application asks for the length of the output buffer
 347                  * to hold the plaintext?
 348                  */
 349                 if (pData == NULL) {
 350                         *pulDataLen = ulEncryptedLen;
 351                         return (CKR_OK);
 352                 }
 353 
 354                 /* Is the application-supplied buffer large enough? */
 355                 if (*pulDataLen < ulEncryptedLen) {
 356                         *pulDataLen = ulEncryptedLen;
 357                         return (CKR_BUFFER_TOO_SMALL);
 358                 }
 359                 out_len = ulEncryptedLen;
 360                 in_buf = pEncrypted;
 361                 out_buf = pData;
 362         } else {
 363                 /*
 364                  * Called by C_DecryptUpdate
 365                  *
 366                  * Add the lengths of last remaining data and current
 367                  * input data together to get the total input length.
 368                  */
 369                 total_len = soft_blowfish_ctx->remain_len + ulEncryptedLen;
 370 
 371                 if (total_len < BLOWFISH_BLOCK_LEN) {
 372                         if (pData != NULL) {
 373                                 (void) memcpy(soft_blowfish_ctx->data +
 374                                     soft_blowfish_ctx->remain_len,
 375                                     pEncrypted, ulEncryptedLen);
 376 
 377                                 soft_blowfish_ctx->remain_len += ulEncryptedLen;
 378                         }
 379 
 380                         /* Set output data length to 0. */
 381                         *pulDataLen = 0;
 382                         return (CKR_OK);
 383                 }
 384 
 385                 /* Compute the length of remaining data. */
 386                 remain = total_len % BLOWFISH_BLOCK_LEN;
 387 
 388                 /*
 389                  * Make sure that the output length is a multiple of
 390                  * blocksize.
 391                  */
 392                 out_len = total_len - remain;
 393 
 394                 /*
 395                  * if application asks for the length of the output buffer
 396                  * to hold the plaintext?
 397                  */
 398                 if (pData == NULL) {
 399                         *pulDataLen = out_len;
 400                         return (CKR_OK);
 401                 }
 402 
 403                 /*
 404                  * Is the application-supplied buffer large enough?
 405                  */
 406                 if (*pulDataLen < out_len) {
 407                         *pulDataLen = out_len;
 408                         return (CKR_BUFFER_TOO_SMALL);
 409                 }
 410 
 411                 if (soft_blowfish_ctx->remain_len != 0) {
 412                         /*
 413                          * Copy last remaining data and current input data
 414                          * to the output buffer.
 415                          */
 416                         (void) memmove(pData + soft_blowfish_ctx->remain_len,
 417                             pEncrypted,
 418                             out_len - soft_blowfish_ctx->remain_len);
 419                         (void) memcpy(pData, soft_blowfish_ctx->data,
 420                             soft_blowfish_ctx->remain_len);
 421                         bzero(soft_blowfish_ctx->data,
 422                             soft_blowfish_ctx->remain_len);
 423 
 424 
 425                         in_buf = pData;
 426                 } else {
 427                         in_buf = pEncrypted;
 428                 }
 429 
 430                 out_buf = pData;
 431         }
 432 
 433         out.cd_format = CRYPTO_DATA_RAW;
 434         out.cd_offset = 0;
 435         out.cd_length = out_len;
 436         out.cd_raw.iov_base = (char *)out_buf;
 437         out.cd_raw.iov_len = out_len;
 438 
 439         /* Decrypt multiple blocks of data. */
 440         rc = blowfish_decrypt_contiguous_blocks(
 441                 (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc,
 442                 (char *)in_buf, out_len, &out);
 443 
 444         if (rc == 0) {
 445                 *pulDataLen = out_len;
 446                 if (update) {
 447                         /*
 448                          * For decrypt update, if there is remaining data,
 449                          * save it and its length in the context.
 450                          */
 451                         if (remain != 0)
 452                                 (void) memcpy(soft_blowfish_ctx->data,
 453                                     pEncrypted + (ulEncryptedLen - remain),
 454                                     remain);
 455                         soft_blowfish_ctx->remain_len = remain;
 456                         return (CKR_OK);
 457                 }
 458 
 459 
 460         } else {
 461                 *pulDataLen = 0;
 462                 rv = CKR_FUNCTION_FAILED;
 463         }
 464 
 465 cleanup:
 466         (void) pthread_mutex_lock(&session_p->session_mutex);
 467         blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc;
 468         if (blowfish_ctx != NULL) {
 469                 bzero(blowfish_ctx->bc_keysched,
 470                     blowfish_ctx->bc_keysched_len);
 471                 free(soft_blowfish_ctx->blowfish_cbc);
 472         }
 473 
 474         bzero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len);
 475         free(soft_blowfish_ctx->key_sched);
 476         free(session_p->decrypt.context);
 477         session_p->decrypt.context = NULL;
 478         (void) pthread_mutex_unlock(&session_p->session_mutex);
 479 
 480         return (rv);
 481 }
 482 
 483 /*
 484  * Allocate and initialize a context for BLOWFISH CBC mode of operation.
 485  */
 486 
 487 void *
 488 blowfish_cbc_ctx_init(void *key_sched, size_t size, uint8_t *ivec)
 489 {
 490 
 491         cbc_ctx_t *cbc_ctx;
 492 
 493         if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
 494                 return (NULL);
 495 
 496         cbc_ctx->cbc_keysched = key_sched;
 497 
 498         (void) memcpy(&cbc_ctx->cbc_iv[0], ivec, BLOWFISH_BLOCK_LEN);
 499 
 500         cbc_ctx->cbc_lastp = (uint8_t *)&(cbc_ctx->cbc_iv);
 501         cbc_ctx->cbc_keysched_len = size;
 502         cbc_ctx->cbc_flags |= CBC_MODE;
 503 
 504         return (cbc_ctx);
 505 }