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