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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 26 */ 27 28 #ifndef _KERNEL 29 #include <stdlib.h> 30 #include <assert.h> 31 #include <strings.h> 32 #endif 33 34 #include <sys/strsun.h> 35 #include <sys/types.h> 36 #include <modes/modes.h> 37 #include <sys/crypto/common.h> 38 #include <sys/crypto/impl.h> 39 40 /* 41 * Initialize by setting iov_or_mp to point to the current iovec or mp, 42 * and by setting current_offset to an offset within the current iovec or mp. 43 */ 44 void 45 crypto_init_ptrs(crypto_data_t *out, void **iov_or_mp, offset_t *current_offset) 46 { 47 offset_t offset; 48 49 switch (out->cd_format) { 50 case CRYPTO_DATA_RAW: 51 *current_offset = out->cd_offset; 52 break; 53 54 case CRYPTO_DATA_UIO: { 55 uio_t *uiop = out->cd_uio; 56 uintptr_t vec_idx; 57 58 offset = out->cd_offset; 59 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt && 60 offset >= uiop->uio_iov[vec_idx].iov_len; 61 offset -= uiop->uio_iov[vec_idx++].iov_len) 62 ; 63 64 *current_offset = offset; 65 *iov_or_mp = (void *)vec_idx; 66 break; 67 } 68 69 case CRYPTO_DATA_MBLK: { 70 mblk_t *mp; 71 72 offset = out->cd_offset; 73 for (mp = out->cd_mp; mp != NULL && offset >= MBLKL(mp); 74 offset -= MBLKL(mp), mp = mp->b_cont) 75 ; 76 77 *current_offset = offset; 78 *iov_or_mp = mp; 79 break; 80 81 } 82 } /* end switch */ 83 } 84 85 /* 86 * Get pointers for where in the output to copy a block of encrypted or 87 * decrypted data. The iov_or_mp argument stores a pointer to the current 88 * iovec or mp, and offset stores an offset into the current iovec or mp. 89 */ 90 void 91 crypto_get_ptrs(crypto_data_t *out, void **iov_or_mp, offset_t *current_offset, 92 uint8_t **out_data_1, size_t *out_data_1_len, uint8_t **out_data_2, 93 size_t amt) 94 { 95 offset_t offset; 96 97 switch (out->cd_format) { 98 case CRYPTO_DATA_RAW: { 99 iovec_t *iov; 100 101 offset = *current_offset; 102 iov = &out->cd_raw; 103 if ((offset + amt) <= iov->iov_len) { 104 /* one block fits */ 105 *out_data_1 = (uint8_t *)iov->iov_base + offset; 106 *out_data_1_len = amt; 107 *out_data_2 = NULL; 108 *current_offset = offset + amt; 109 } 110 break; 111 } 112 113 case CRYPTO_DATA_UIO: { 114 uio_t *uio = out->cd_uio; 115 iovec_t *iov; 116 offset_t offset; 117 uintptr_t vec_idx; 118 uint8_t *p; 119 120 offset = *current_offset; 121 vec_idx = (uintptr_t)(*iov_or_mp); 122 iov = &uio->uio_iov[vec_idx]; 123 p = (uint8_t *)iov->iov_base + offset; 124 *out_data_1 = p; 125 126 if (offset + amt <= iov->iov_len) { 127 /* can fit one block into this iov */ 128 *out_data_1_len = amt; 129 *out_data_2 = NULL; 130 *current_offset = offset + amt; 131 } else { 132 /* one block spans two iovecs */ 133 *out_data_1_len = iov->iov_len - offset; 134 if (vec_idx == uio->uio_iovcnt) 135 return; 136 vec_idx++; 137 iov = &uio->uio_iov[vec_idx]; 138 *out_data_2 = (uint8_t *)iov->iov_base; 139 *current_offset = amt - *out_data_1_len; 140 } 141 *iov_or_mp = (void *)vec_idx; 142 break; 143 } 144 145 case CRYPTO_DATA_MBLK: { 146 mblk_t *mp; 147 uint8_t *p; 148 149 offset = *current_offset; 150 mp = (mblk_t *)*iov_or_mp; 151 p = mp->b_rptr + offset; 152 *out_data_1 = p; 153 if ((p + amt) <= mp->b_wptr) { 154 /* can fit one block into this mblk */ 155 *out_data_1_len = amt; 156 *out_data_2 = NULL; 157 *current_offset = offset + amt; 158 } else { 159 /* one block spans two mblks */ 160 *out_data_1_len = _PTRDIFF(mp->b_wptr, p); 161 if ((mp = mp->b_cont) == NULL) 162 return; 163 *out_data_2 = mp->b_rptr; 164 *current_offset = (amt - *out_data_1_len); 165 } 166 *iov_or_mp = mp; 167 break; 168 } 169 } /* end switch */ 170 } 171 172 void 173 crypto_free_mode_ctx(void *ctx) 174 { 175 common_ctx_t *common_ctx = (common_ctx_t *)ctx; 176 177 switch (common_ctx->cc_flags & (ECB_MODE|CBC_MODE|CMAC_MODE|CTR_MODE| 178 CCM_MODE|GCM_MODE|GMAC_MODE)) { 179 case ECB_MODE: 180 #ifdef _KERNEL 181 kmem_free(common_ctx, sizeof (ecb_ctx_t)); 182 #else 183 free(common_ctx); 184 #endif 185 break; 186 187 case CBC_MODE: 188 case CMAC_MODE: 189 #ifdef _KERNEL 190 kmem_free(common_ctx, sizeof (cbc_ctx_t)); 191 #else 192 free(common_ctx); 193 #endif 194 break; 195 196 case CTR_MODE: 197 #ifdef _KERNEL 198 kmem_free(common_ctx, sizeof (ctr_ctx_t)); 199 #else 200 free(common_ctx); 201 #endif 202 break; 203 204 case CCM_MODE: 205 #ifdef _KERNEL 206 if (((ccm_ctx_t *)ctx)->ccm_pt_buf != NULL) 207 kmem_free(((ccm_ctx_t *)ctx)->ccm_pt_buf, 208 ((ccm_ctx_t *)ctx)->ccm_data_len); 209 210 kmem_free(ctx, sizeof (ccm_ctx_t)); 211 #else 212 if (((ccm_ctx_t *)ctx)->ccm_pt_buf != NULL) 213 free(((ccm_ctx_t *)ctx)->ccm_pt_buf); 214 free(ctx); 215 #endif 216 break; 217 218 case GCM_MODE: 219 case GMAC_MODE: 220 #ifdef _KERNEL 221 if (((gcm_ctx_t *)ctx)->gcm_pt_buf != NULL) 222 kmem_free(((gcm_ctx_t *)ctx)->gcm_pt_buf, 223 ((gcm_ctx_t *)ctx)->gcm_pt_buf_len); 224 225 kmem_free(ctx, sizeof (gcm_ctx_t)); 226 #else 227 if (((gcm_ctx_t *)ctx)->gcm_pt_buf != NULL) 228 free(((gcm_ctx_t *)ctx)->gcm_pt_buf); 229 free(ctx); 230 #endif 231 } 232 } 233 234 /* 235 * Utility routine to apply the command, 'cmd', to the 236 * data in the uio structure. 237 */ 238 int 239 crypto_uio_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd, 240 void *digest_ctx, void (*update)()) 241 { 242 uio_t *uiop = data->cd_uio; 243 off_t offset = data->cd_offset; 244 size_t length = len; 245 uint_t vec_idx; 246 size_t cur_len; 247 uchar_t *datap; 248 249 #ifdef _KERNEL 250 ASSERT3U(data->cd_format, ==, CRYPTO_DATA_UIO); 251 #else 252 assert(data->cd_format == CRYPTO_DATA_UIO); 253 #endif 254 if (uiop->uio_segflg != UIO_SYSSPACE) { 255 return (CRYPTO_ARGUMENTS_BAD); 256 } 257 258 /* 259 * Jump to the first iovec containing data to be 260 * processed. 261 */ 262 for (vec_idx = 0; vec_idx < uiop->uio_iovcnt && 263 offset >= uiop->uio_iov[vec_idx].iov_len; 264 offset -= uiop->uio_iov[vec_idx++].iov_len) 265 ; 266 267 if (vec_idx == uiop->uio_iovcnt) { 268 /* 269 * The caller specified an offset that is larger than 270 * the total size of the buffers it provided. 271 */ 272 return (CRYPTO_DATA_LEN_RANGE); 273 } 274 275 while (vec_idx < uiop->uio_iovcnt && length > 0) { 276 cur_len = MIN(uiop->uio_iov[vec_idx].iov_len - 277 offset, length); 278 279 datap = (uchar_t *)(uiop->uio_iov[vec_idx].iov_base + 280 offset); 281 switch (cmd) { 282 case COPY_FROM_DATA: 283 bcopy(datap, buf, cur_len); 284 buf += cur_len; 285 break; 286 case COPY_TO_DATA: 287 bcopy(buf, datap, cur_len); 288 buf += cur_len; 289 break; 290 case COMPARE_TO_DATA: 291 if (bcmp(datap, buf, cur_len)) 292 return (CRYPTO_SIGNATURE_INVALID); 293 buf += cur_len; 294 break; 295 case MD5_DIGEST_DATA: 296 case SHA1_DIGEST_DATA: 297 case SHA2_DIGEST_DATA: 298 case GHASH_DATA: 299 update(digest_ctx, datap, cur_len); 300 break; 301 } 302 303 length -= cur_len; 304 vec_idx++; 305 offset = 0; 306 } 307 308 if (vec_idx == uiop->uio_iovcnt && length > 0) { 309 /* 310 * The end of the specified iovec's was reached but 311 * the length requested could not be processed. 312 */ 313 switch (cmd) { 314 case COPY_TO_DATA: 315 data->cd_length = len; 316 return (CRYPTO_BUFFER_TOO_SMALL); 317 default: 318 return (CRYPTO_DATA_LEN_RANGE); 319 } 320 } 321 322 return (CRYPTO_SUCCESS); 323 } 324 325 /* 326 * Utility routine to apply the command, 'cmd', to the 327 * data in the mblk structure. 328 */ 329 int 330 crypto_mblk_data(crypto_data_t *data, uchar_t *buf, int len, cmd_type_t cmd, 331 void *digest_ctx, void (*update)()) 332 { 333 off_t offset = data->cd_offset; 334 size_t length = len; 335 mblk_t *mp; 336 size_t cur_len; 337 uchar_t *datap; 338 339 #ifdef _KERNEL 340 ASSERT3U(data->cd_format, ==, CRYPTO_DATA_MBLK); 341 #else 342 assert(data->cd_format == CRYPTO_DATA_MBLK); 343 #endif 344 /* 345 * Jump to the first mblk_t containing data to be processed. 346 */ 347 for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp); 348 offset -= MBLKL(mp), mp = mp->b_cont) 349 ; 350 if (mp == NULL) { 351 /* 352 * The caller specified an offset that is larger 353 * than the total size of the buffers it provided. 354 */ 355 return (CRYPTO_DATA_LEN_RANGE); 356 } 357 358 /* 359 * Now do the processing on the mblk chain. 360 */ 361 while (mp != NULL && length > 0) { 362 cur_len = MIN(MBLKL(mp) - offset, length); 363 364 datap = (uchar_t *)(mp->b_rptr + offset); 365 switch (cmd) { 366 case COPY_FROM_DATA: 367 bcopy(datap, buf, cur_len); 368 buf += cur_len; 369 break; 370 case COPY_TO_DATA: 371 bcopy(buf, datap, cur_len); 372 buf += cur_len; 373 break; 374 case COMPARE_TO_DATA: 375 if (bcmp(datap, buf, cur_len)) 376 return (CRYPTO_SIGNATURE_INVALID); 377 buf += cur_len; 378 break; 379 case MD5_DIGEST_DATA: 380 case SHA1_DIGEST_DATA: 381 case SHA2_DIGEST_DATA: 382 case GHASH_DATA: 383 update(digest_ctx, datap, cur_len); 384 break; 385 } 386 387 length -= cur_len; 388 offset = 0; 389 mp = mp->b_cont; 390 } 391 392 if (mp == NULL && length > 0) { 393 /* 394 * The end of the mblk was reached but the length 395 * requested could not be processed. 396 */ 397 switch (cmd) { 398 case COPY_TO_DATA: 399 data->cd_length = len; 400 return (CRYPTO_BUFFER_TOO_SMALL); 401 default: 402 return (CRYPTO_DATA_LEN_RANGE); 403 } 404 } 405 406 return (CRYPTO_SUCCESS); 407 } 408 409 /* 410 * Utility routine to copy a buffer to a crypto_data structure. 411 */ 412 int 413 crypto_put_output_data(uchar_t *buf, crypto_data_t *output, int len) 414 { 415 switch (output->cd_format) { 416 case CRYPTO_DATA_RAW: 417 if (MAXOFF_T - output->cd_offset < (off_t)len) { 418 return (CRYPTO_ARGUMENTS_BAD); 419 } 420 if (output->cd_raw.iov_len < len + output->cd_offset) { 421 output->cd_length = len; 422 return (CRYPTO_BUFFER_TOO_SMALL); 423 } 424 bcopy(buf, (uchar_t *)(output->cd_raw.iov_base + 425 output->cd_offset), len); 426 break; 427 428 case CRYPTO_DATA_UIO: 429 return (crypto_uio_data(output, buf, len, 430 COPY_TO_DATA, NULL, NULL)); 431 432 case CRYPTO_DATA_MBLK: 433 return (crypto_mblk_data(output, buf, len, 434 COPY_TO_DATA, NULL, NULL)); 435 436 default: 437 return (CRYPTO_ARGUMENTS_BAD); 438 } 439 440 return (CRYPTO_SUCCESS); 441 } 442