17188Smcpowers /* 27188Smcpowers * CDDL HEADER START 37188Smcpowers * 47188Smcpowers * The contents of this file are subject to the terms of the 57188Smcpowers * Common Development and Distribution License (the "License"). 67188Smcpowers * You may not use this file except in compliance with the License. 77188Smcpowers * 87188Smcpowers * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97188Smcpowers * or http://www.opensolaris.org/os/licensing. 107188Smcpowers * See the License for the specific language governing permissions 117188Smcpowers * and limitations under the License. 127188Smcpowers * 137188Smcpowers * When distributing Covered Code, include this CDDL HEADER in each 147188Smcpowers * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157188Smcpowers * If applicable, add the following below this CDDL HEADER, with the 167188Smcpowers * fields enclosed by brackets "[]" replaced with your own identifying 177188Smcpowers * information: Portions Copyright [yyyy] [name of copyright owner] 187188Smcpowers * 197188Smcpowers * CDDL HEADER END 207188Smcpowers */ 217188Smcpowers /* 227188Smcpowers * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237188Smcpowers * Use is subject to license terms. 247188Smcpowers */ 257188Smcpowers 267188Smcpowers #ifndef _KERNEL 277188Smcpowers #include <strings.h> 287188Smcpowers #include <limits.h> 297188Smcpowers #include <assert.h> 307188Smcpowers #include <security/cryptoki.h> 317188Smcpowers #endif 327188Smcpowers 337188Smcpowers #include <sys/types.h> 347188Smcpowers #include <modes/modes.h> 357188Smcpowers #include <sys/crypto/common.h> 367188Smcpowers #include <sys/crypto/impl.h> 377188Smcpowers 387188Smcpowers /* 397188Smcpowers * Algorithm independent CBC functions. 407188Smcpowers */ 417188Smcpowers int 427188Smcpowers cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, 437188Smcpowers crypto_data_t *out, size_t block_size, 447188Smcpowers int (*encrypt)(const void *, const uint8_t *, uint8_t *), 457188Smcpowers void (*copy_block)(uint8_t *, uint8_t *), 467188Smcpowers void (*xor_block)(uint8_t *, uint8_t *)) 477188Smcpowers { 487188Smcpowers size_t remainder = length; 497188Smcpowers size_t need; 507188Smcpowers uint8_t *datap = (uint8_t *)data; 517188Smcpowers uint8_t *blockp; 527188Smcpowers uint8_t *lastp; 537188Smcpowers void *iov_or_mp; 547188Smcpowers offset_t offset; 557188Smcpowers uint8_t *out_data_1; 567188Smcpowers uint8_t *out_data_2; 577188Smcpowers size_t out_data_1_len; 587188Smcpowers 59*7581SMark.Powers@Sun.COM if (length + ctx->cbc_remainder_len < block_size) { 607188Smcpowers /* accumulate bytes here and return */ 617188Smcpowers bcopy(datap, 62*7581SMark.Powers@Sun.COM (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len, 637188Smcpowers length); 64*7581SMark.Powers@Sun.COM ctx->cbc_remainder_len += length; 65*7581SMark.Powers@Sun.COM ctx->cbc_copy_to = datap; 667188Smcpowers return (CRYPTO_SUCCESS); 677188Smcpowers } 687188Smcpowers 69*7581SMark.Powers@Sun.COM lastp = (uint8_t *)ctx->cbc_iv; 707188Smcpowers if (out != NULL) 717188Smcpowers crypto_init_ptrs(out, &iov_or_mp, &offset); 727188Smcpowers 737188Smcpowers do { 747188Smcpowers /* Unprocessed data from last call. */ 75*7581SMark.Powers@Sun.COM if (ctx->cbc_remainder_len > 0) { 76*7581SMark.Powers@Sun.COM need = block_size - ctx->cbc_remainder_len; 777188Smcpowers 787188Smcpowers if (need > remainder) 797188Smcpowers return (CRYPTO_DATA_LEN_RANGE); 807188Smcpowers 81*7581SMark.Powers@Sun.COM bcopy(datap, &((uint8_t *)ctx->cbc_remainder) 82*7581SMark.Powers@Sun.COM [ctx->cbc_remainder_len], need); 837188Smcpowers 84*7581SMark.Powers@Sun.COM blockp = (uint8_t *)ctx->cbc_remainder; 857188Smcpowers } else { 867188Smcpowers blockp = datap; 877188Smcpowers } 887188Smcpowers 897188Smcpowers if (out == NULL) { 907188Smcpowers /* 917188Smcpowers * XOR the previous cipher block or IV with the 927188Smcpowers * current clear block. 937188Smcpowers */ 947188Smcpowers xor_block(lastp, blockp); 95*7581SMark.Powers@Sun.COM encrypt(ctx->cbc_keysched, blockp, blockp); 967188Smcpowers 97*7581SMark.Powers@Sun.COM ctx->cbc_lastp = blockp; 987188Smcpowers lastp = blockp; 997188Smcpowers 100*7581SMark.Powers@Sun.COM if (ctx->cbc_remainder_len > 0) { 101*7581SMark.Powers@Sun.COM bcopy(blockp, ctx->cbc_copy_to, 102*7581SMark.Powers@Sun.COM ctx->cbc_remainder_len); 103*7581SMark.Powers@Sun.COM bcopy(blockp + ctx->cbc_remainder_len, datap, 1047188Smcpowers need); 1057188Smcpowers } 1067188Smcpowers } else { 1077188Smcpowers /* 1087188Smcpowers * XOR the previous cipher block or IV with the 1097188Smcpowers * current clear block. 1107188Smcpowers */ 1117188Smcpowers xor_block(blockp, lastp); 112*7581SMark.Powers@Sun.COM encrypt(ctx->cbc_keysched, lastp, lastp); 1137188Smcpowers crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 1147188Smcpowers &out_data_1_len, &out_data_2, block_size); 1157188Smcpowers 1167188Smcpowers /* copy block to where it belongs */ 1177188Smcpowers if (out_data_1_len == block_size) { 1187188Smcpowers copy_block(lastp, out_data_1); 1197188Smcpowers } else { 1207188Smcpowers bcopy(lastp, out_data_1, out_data_1_len); 1217188Smcpowers if (out_data_2 != NULL) { 1227188Smcpowers bcopy(lastp + out_data_1_len, 1237188Smcpowers out_data_2, 1247188Smcpowers block_size - out_data_1_len); 1257188Smcpowers } 1267188Smcpowers } 1277188Smcpowers /* update offset */ 1287188Smcpowers out->cd_offset += block_size; 1297188Smcpowers } 1307188Smcpowers 1317188Smcpowers /* Update pointer to next block of data to be processed. */ 132*7581SMark.Powers@Sun.COM if (ctx->cbc_remainder_len != 0) { 1337188Smcpowers datap += need; 134*7581SMark.Powers@Sun.COM ctx->cbc_remainder_len = 0; 1357188Smcpowers } else { 1367188Smcpowers datap += block_size; 1377188Smcpowers } 1387188Smcpowers 1397188Smcpowers remainder = (size_t)&data[length] - (size_t)datap; 1407188Smcpowers 1417188Smcpowers /* Incomplete last block. */ 1427188Smcpowers if (remainder > 0 && remainder < block_size) { 143*7581SMark.Powers@Sun.COM bcopy(datap, ctx->cbc_remainder, remainder); 144*7581SMark.Powers@Sun.COM ctx->cbc_remainder_len = remainder; 145*7581SMark.Powers@Sun.COM ctx->cbc_copy_to = datap; 1467188Smcpowers goto out; 1477188Smcpowers } 148*7581SMark.Powers@Sun.COM ctx->cbc_copy_to = NULL; 1497188Smcpowers 1507188Smcpowers } while (remainder > 0); 1517188Smcpowers 1527188Smcpowers out: 1537188Smcpowers /* 1547188Smcpowers * Save the last encrypted block in the context. 1557188Smcpowers */ 156*7581SMark.Powers@Sun.COM if (ctx->cbc_lastp != NULL) { 157*7581SMark.Powers@Sun.COM copy_block((uint8_t *)ctx->cbc_lastp, (uint8_t *)ctx->cbc_iv); 158*7581SMark.Powers@Sun.COM ctx->cbc_lastp = (uint8_t *)ctx->cbc_iv; 1597188Smcpowers } 1607188Smcpowers 1617188Smcpowers return (CRYPTO_SUCCESS); 1627188Smcpowers } 1637188Smcpowers 1647188Smcpowers #define OTHER(a, ctx) \ 165*7581SMark.Powers@Sun.COM (((a) == (ctx)->cbc_lastblock) ? (ctx)->cbc_iv : (ctx)->cbc_lastblock) 1667188Smcpowers 1677188Smcpowers /* ARGSUSED */ 1687188Smcpowers int 1697188Smcpowers cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length, 1707188Smcpowers crypto_data_t *out, size_t block_size, 1717188Smcpowers int (*decrypt)(const void *, const uint8_t *, uint8_t *), 1727188Smcpowers void (*copy_block)(uint8_t *, uint8_t *), 1737188Smcpowers void (*xor_block)(uint8_t *, uint8_t *)) 1747188Smcpowers { 1757188Smcpowers size_t remainder = length; 1767188Smcpowers size_t need; 1777188Smcpowers uint8_t *datap = (uint8_t *)data; 1787188Smcpowers uint8_t *blockp; 1797188Smcpowers uint8_t *lastp; 1807188Smcpowers void *iov_or_mp; 1817188Smcpowers offset_t offset; 1827188Smcpowers uint8_t *out_data_1; 1837188Smcpowers uint8_t *out_data_2; 1847188Smcpowers size_t out_data_1_len; 1857188Smcpowers 186*7581SMark.Powers@Sun.COM if (length + ctx->cbc_remainder_len < block_size) { 1877188Smcpowers /* accumulate bytes here and return */ 1887188Smcpowers bcopy(datap, 189*7581SMark.Powers@Sun.COM (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len, 1907188Smcpowers length); 191*7581SMark.Powers@Sun.COM ctx->cbc_remainder_len += length; 192*7581SMark.Powers@Sun.COM ctx->cbc_copy_to = datap; 1937188Smcpowers return (CRYPTO_SUCCESS); 1947188Smcpowers } 1957188Smcpowers 196*7581SMark.Powers@Sun.COM lastp = ctx->cbc_lastp; 1977188Smcpowers if (out != NULL) 1987188Smcpowers crypto_init_ptrs(out, &iov_or_mp, &offset); 1997188Smcpowers 2007188Smcpowers do { 2017188Smcpowers /* Unprocessed data from last call. */ 202*7581SMark.Powers@Sun.COM if (ctx->cbc_remainder_len > 0) { 203*7581SMark.Powers@Sun.COM need = block_size - ctx->cbc_remainder_len; 2047188Smcpowers 2057188Smcpowers if (need > remainder) 2067188Smcpowers return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE); 2077188Smcpowers 208*7581SMark.Powers@Sun.COM bcopy(datap, &((uint8_t *)ctx->cbc_remainder) 209*7581SMark.Powers@Sun.COM [ctx->cbc_remainder_len], need); 2107188Smcpowers 211*7581SMark.Powers@Sun.COM blockp = (uint8_t *)ctx->cbc_remainder; 2127188Smcpowers } else { 2137188Smcpowers blockp = datap; 2147188Smcpowers } 2157188Smcpowers 2167188Smcpowers /* LINTED: pointer alignment */ 2177188Smcpowers copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx)); 2187188Smcpowers 2197188Smcpowers if (out != NULL) { 220*7581SMark.Powers@Sun.COM decrypt(ctx->cbc_keysched, blockp, 221*7581SMark.Powers@Sun.COM (uint8_t *)ctx->cbc_remainder); 222*7581SMark.Powers@Sun.COM blockp = (uint8_t *)ctx->cbc_remainder; 2237188Smcpowers } else { 224*7581SMark.Powers@Sun.COM decrypt(ctx->cbc_keysched, blockp, blockp); 2257188Smcpowers } 2267188Smcpowers 2277188Smcpowers /* 2287188Smcpowers * XOR the previous cipher block or IV with the 2297188Smcpowers * currently decrypted block. 2307188Smcpowers */ 2317188Smcpowers xor_block(lastp, blockp); 2327188Smcpowers 2337188Smcpowers /* LINTED: pointer alignment */ 2347188Smcpowers lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx); 2357188Smcpowers 2367188Smcpowers if (out != NULL) { 2377188Smcpowers crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 2387188Smcpowers &out_data_1_len, &out_data_2, block_size); 2397188Smcpowers 2407188Smcpowers bcopy(blockp, out_data_1, out_data_1_len); 2417188Smcpowers if (out_data_2 != NULL) { 2427188Smcpowers bcopy(blockp + out_data_1_len, out_data_2, 2437188Smcpowers block_size - out_data_1_len); 2447188Smcpowers } 2457188Smcpowers 2467188Smcpowers /* update offset */ 2477188Smcpowers out->cd_offset += block_size; 2487188Smcpowers 249*7581SMark.Powers@Sun.COM } else if (ctx->cbc_remainder_len > 0) { 2507188Smcpowers /* copy temporary block to where it belongs */ 251*7581SMark.Powers@Sun.COM bcopy(blockp, ctx->cbc_copy_to, ctx->cbc_remainder_len); 252*7581SMark.Powers@Sun.COM bcopy(blockp + ctx->cbc_remainder_len, datap, need); 2537188Smcpowers } 2547188Smcpowers 2557188Smcpowers /* Update pointer to next block of data to be processed. */ 256*7581SMark.Powers@Sun.COM if (ctx->cbc_remainder_len != 0) { 2577188Smcpowers datap += need; 258*7581SMark.Powers@Sun.COM ctx->cbc_remainder_len = 0; 2597188Smcpowers } else { 2607188Smcpowers datap += block_size; 2617188Smcpowers } 2627188Smcpowers 2637188Smcpowers remainder = (size_t)&data[length] - (size_t)datap; 2647188Smcpowers 2657188Smcpowers /* Incomplete last block. */ 2667188Smcpowers if (remainder > 0 && remainder < block_size) { 267*7581SMark.Powers@Sun.COM bcopy(datap, ctx->cbc_remainder, remainder); 268*7581SMark.Powers@Sun.COM ctx->cbc_remainder_len = remainder; 269*7581SMark.Powers@Sun.COM ctx->cbc_lastp = lastp; 270*7581SMark.Powers@Sun.COM ctx->cbc_copy_to = datap; 2717188Smcpowers return (CRYPTO_SUCCESS); 2727188Smcpowers } 273*7581SMark.Powers@Sun.COM ctx->cbc_copy_to = NULL; 2747188Smcpowers 2757188Smcpowers } while (remainder > 0); 2767188Smcpowers 277*7581SMark.Powers@Sun.COM ctx->cbc_lastp = lastp; 2787188Smcpowers return (CRYPTO_SUCCESS); 2797188Smcpowers } 2807188Smcpowers 2817188Smcpowers int 2827188Smcpowers cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len, 2837188Smcpowers size_t block_size, void (*copy_block)(uint8_t *, uint64_t *)) 2847188Smcpowers { 2857188Smcpowers /* 2867188Smcpowers * Copy IV into context. 2877188Smcpowers * 2887188Smcpowers * If cm_param == NULL then the IV comes from the 2897188Smcpowers * cd_miscdata field in the crypto_data structure. 2907188Smcpowers */ 2917188Smcpowers if (param != NULL) { 2927188Smcpowers #ifdef _KERNEL 2937188Smcpowers ASSERT(param_len == block_size); 2947188Smcpowers #else 2957188Smcpowers assert(param_len == block_size); 2967188Smcpowers #endif 297*7581SMark.Powers@Sun.COM copy_block((uchar_t *)param, cbc_ctx->cbc_iv); 2987188Smcpowers } 2997188Smcpowers 300*7581SMark.Powers@Sun.COM cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0]; 301*7581SMark.Powers@Sun.COM cbc_ctx->cbc_flags |= CBC_MODE; 3027188Smcpowers return (CRYPTO_SUCCESS); 3037188Smcpowers } 3047188Smcpowers 3057188Smcpowers /* ARGSUSED */ 3067188Smcpowers void * 3077188Smcpowers cbc_alloc_ctx(int kmflag) 3087188Smcpowers { 3097188Smcpowers cbc_ctx_t *cbc_ctx; 3107188Smcpowers 3117188Smcpowers #ifdef _KERNEL 3127188Smcpowers if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL) 3137188Smcpowers #else 3147188Smcpowers if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL) 3157188Smcpowers #endif 3167188Smcpowers return (NULL); 3177188Smcpowers 318*7581SMark.Powers@Sun.COM cbc_ctx->cbc_flags = CBC_MODE; 3197188Smcpowers return (cbc_ctx); 3207188Smcpowers } 321