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 38*7421SDaniel.Anderson@Sun.COM #ifdef _LITTLE_ENDIAN 39*7421SDaniel.Anderson@Sun.COM #include <sys/byteorder.h> 40*7421SDaniel.Anderson@Sun.COM #endif 41*7421SDaniel.Anderson@Sun.COM 427188Smcpowers /* 437188Smcpowers * Encrypt and decrypt multiple blocks of data in counter mode. 447188Smcpowers */ 457188Smcpowers int 467188Smcpowers ctr_mode_contiguous_blocks(ctr_ctx_t *ctx, char *data, size_t length, 477188Smcpowers crypto_data_t *out, size_t block_size, 487188Smcpowers int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct), 497188Smcpowers void (*xor_block)(uint8_t *, uint8_t *)) 507188Smcpowers { 517188Smcpowers size_t remainder = length; 527188Smcpowers size_t need; 537188Smcpowers uint8_t *datap = (uint8_t *)data; 547188Smcpowers uint8_t *blockp; 557188Smcpowers uint8_t *lastp; 567188Smcpowers void *iov_or_mp; 577188Smcpowers offset_t offset; 587188Smcpowers uint8_t *out_data_1; 597188Smcpowers uint8_t *out_data_2; 607188Smcpowers size_t out_data_1_len; 617188Smcpowers uint64_t counter; 627188Smcpowers #ifdef _LITTLE_ENDIAN 637188Smcpowers uint8_t *p; 647188Smcpowers #endif 657188Smcpowers 667188Smcpowers if (length + ctx->ctr_remainder_len < block_size) { 677188Smcpowers /* accumulate bytes here and return */ 687188Smcpowers bcopy(datap, 697188Smcpowers (uint8_t *)ctx->ctr_remainder + ctx->ctr_remainder_len, 707188Smcpowers length); 717188Smcpowers ctx->ctr_remainder_len += length; 727188Smcpowers ctx->ctr_copy_to = datap; 737188Smcpowers return (CRYPTO_SUCCESS); 747188Smcpowers } 757188Smcpowers 767188Smcpowers lastp = (uint8_t *)ctx->ctr_cb; 777188Smcpowers if (out != NULL) 787188Smcpowers crypto_init_ptrs(out, &iov_or_mp, &offset); 797188Smcpowers 807188Smcpowers do { 817188Smcpowers /* Unprocessed data from last call. */ 827188Smcpowers if (ctx->ctr_remainder_len > 0) { 837188Smcpowers need = block_size - ctx->ctr_remainder_len; 847188Smcpowers 857188Smcpowers if (need > remainder) 867188Smcpowers return (CRYPTO_DATA_LEN_RANGE); 877188Smcpowers 887188Smcpowers bcopy(datap, &((uint8_t *)ctx->ctr_remainder) 897188Smcpowers [ctx->ctr_remainder_len], need); 907188Smcpowers 917188Smcpowers blockp = (uint8_t *)ctx->ctr_remainder; 927188Smcpowers } else { 937188Smcpowers blockp = datap; 947188Smcpowers } 957188Smcpowers 967188Smcpowers /* ctr_cb is the counter block */ 977188Smcpowers cipher(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb, 987188Smcpowers (uint8_t *)ctx->ctr_tmp); 997188Smcpowers 1007188Smcpowers lastp = (uint8_t *)ctx->ctr_tmp; 1017188Smcpowers 1027188Smcpowers /* 1037188Smcpowers * Increment counter. Counter bits are confined 1047188Smcpowers * to the bottom 64 bits of the counter block. 1057188Smcpowers */ 106*7421SDaniel.Anderson@Sun.COM #ifdef _LITTLE_ENDIAN 107*7421SDaniel.Anderson@Sun.COM counter = ntohll(ctx->ctr_cb[1] & ctx->ctr_counter_mask); 108*7421SDaniel.Anderson@Sun.COM counter = htonll(counter + 1); 109*7421SDaniel.Anderson@Sun.COM #else 1107188Smcpowers counter = ctx->ctr_cb[1] & ctx->ctr_counter_mask; 1117188Smcpowers counter++; 112*7421SDaniel.Anderson@Sun.COM #endif /* _LITTLE_ENDIAN */ 1137188Smcpowers counter &= ctx->ctr_counter_mask; 1147188Smcpowers ctx->ctr_cb[1] = 1157188Smcpowers (ctx->ctr_cb[1] & ~(ctx->ctr_counter_mask)) | counter; 1167188Smcpowers 1177188Smcpowers /* 1187188Smcpowers * XOR the previous cipher block or IV with the 1197188Smcpowers * current clear block. 1207188Smcpowers */ 1217188Smcpowers xor_block(blockp, lastp); 1227188Smcpowers 1237188Smcpowers if (out == NULL) { 1247188Smcpowers if (ctx->ctr_remainder_len > 0) { 1257188Smcpowers bcopy(lastp, ctx->ctr_copy_to, 1267188Smcpowers ctx->ctr_remainder_len); 1277188Smcpowers bcopy(lastp + ctx->ctr_remainder_len, datap, 1287188Smcpowers need); 1297188Smcpowers } 1307188Smcpowers } else { 1317188Smcpowers crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 1327188Smcpowers &out_data_1_len, &out_data_2, block_size); 1337188Smcpowers 1347188Smcpowers /* copy block to where it belongs */ 1357188Smcpowers bcopy(lastp, out_data_1, out_data_1_len); 1367188Smcpowers if (out_data_2 != NULL) { 1377188Smcpowers bcopy(lastp + out_data_1_len, out_data_2, 1387188Smcpowers block_size - out_data_1_len); 1397188Smcpowers } 1407188Smcpowers /* update offset */ 1417188Smcpowers out->cd_offset += block_size; 1427188Smcpowers } 1437188Smcpowers 1447188Smcpowers /* Update pointer to next block of data to be processed. */ 1457188Smcpowers if (ctx->ctr_remainder_len != 0) { 1467188Smcpowers datap += need; 1477188Smcpowers ctx->ctr_remainder_len = 0; 1487188Smcpowers } else { 1497188Smcpowers datap += block_size; 1507188Smcpowers } 1517188Smcpowers 1527188Smcpowers remainder = (size_t)&data[length] - (size_t)datap; 1537188Smcpowers 1547188Smcpowers /* Incomplete last block. */ 1557188Smcpowers if (remainder > 0 && remainder < block_size) { 1567188Smcpowers bcopy(datap, ctx->ctr_remainder, remainder); 1577188Smcpowers ctx->ctr_remainder_len = remainder; 1587188Smcpowers ctx->ctr_copy_to = datap; 1597188Smcpowers goto out; 1607188Smcpowers } 1617188Smcpowers ctx->ctr_copy_to = NULL; 1627188Smcpowers 1637188Smcpowers } while (remainder > 0); 1647188Smcpowers 1657188Smcpowers out: 1667188Smcpowers return (CRYPTO_SUCCESS); 1677188Smcpowers } 1687188Smcpowers 1697188Smcpowers int 1707188Smcpowers ctr_mode_final(ctr_ctx_t *ctx, crypto_data_t *out, 1717188Smcpowers int (*encrypt_block)(const void *, const uint8_t *, uint8_t *)) 1727188Smcpowers { 1737188Smcpowers uint8_t *lastp; 1747188Smcpowers void *iov_or_mp; 1757188Smcpowers offset_t offset; 1767188Smcpowers uint8_t *out_data_1; 1777188Smcpowers uint8_t *out_data_2; 1787188Smcpowers size_t out_data_1_len; 1797188Smcpowers uint8_t *p; 1807188Smcpowers int i; 1817188Smcpowers 1827188Smcpowers if (out->cd_length < ctx->ctr_remainder_len) 1837188Smcpowers return (CRYPTO_DATA_LEN_RANGE); 1847188Smcpowers 1857188Smcpowers encrypt_block(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb, 1867188Smcpowers (uint8_t *)ctx->ctr_tmp); 1877188Smcpowers 1887188Smcpowers lastp = (uint8_t *)ctx->ctr_tmp; 1897188Smcpowers p = (uint8_t *)ctx->ctr_remainder; 1907188Smcpowers for (i = 0; i < ctx->ctr_remainder_len; i++) { 1917188Smcpowers p[i] ^= lastp[i]; 1927188Smcpowers } 1937188Smcpowers 1947188Smcpowers crypto_init_ptrs(out, &iov_or_mp, &offset); 1957188Smcpowers crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 1967188Smcpowers &out_data_1_len, &out_data_2, ctx->ctr_remainder_len); 1977188Smcpowers 1987188Smcpowers bcopy(p, out_data_1, out_data_1_len); 1997188Smcpowers if (out_data_2 != NULL) { 2007188Smcpowers bcopy((uint8_t *)p + out_data_1_len, 2017188Smcpowers out_data_2, ctx->ctr_remainder_len - out_data_1_len); 2027188Smcpowers } 2037188Smcpowers out->cd_offset += ctx->ctr_remainder_len; 2047188Smcpowers ctx->ctr_remainder_len = 0; 2057188Smcpowers return (CRYPTO_SUCCESS); 2067188Smcpowers } 2077188Smcpowers 2087188Smcpowers int 2097188Smcpowers ctr_init_ctx(ctr_ctx_t *ctr_ctx, ulong_t count, uint8_t *cb, 2107188Smcpowers void (*copy_block)(uint8_t *, uint8_t *)) 2117188Smcpowers { 2127188Smcpowers uint64_t mask = 0; 2137188Smcpowers 2147188Smcpowers if (count == 0 || count > 64) { 2157188Smcpowers return (CRYPTO_MECHANISM_PARAM_INVALID); 2167188Smcpowers } 2177188Smcpowers while (count-- > 0) 2187188Smcpowers mask |= (1ULL << count); 219*7421SDaniel.Anderson@Sun.COM 2207188Smcpowers #ifdef _LITTLE_ENDIAN 221*7421SDaniel.Anderson@Sun.COM mask = htonll(mask); 2227188Smcpowers #endif 2237188Smcpowers ctr_ctx->ctr_counter_mask = mask; 2247188Smcpowers copy_block(cb, (uchar_t *)ctr_ctx->ctr_cb); 2257188Smcpowers ctr_ctx->ctr_lastp = (uint8_t *)&ctr_ctx->ctr_cb[0]; 2267188Smcpowers ctr_ctx->ctr_flags |= CTR_MODE; 2277188Smcpowers return (CRYPTO_SUCCESS); 2287188Smcpowers } 2297188Smcpowers 2307188Smcpowers /* ARGSUSED */ 2317188Smcpowers void * 2327188Smcpowers ctr_alloc_ctx(int kmflag) 2337188Smcpowers { 2347188Smcpowers ctr_ctx_t *ctr_ctx; 2357188Smcpowers 2367188Smcpowers #ifdef _KERNEL 2377188Smcpowers if ((ctr_ctx = kmem_zalloc(sizeof (ctr_ctx_t), kmflag)) == NULL) 2387188Smcpowers #else 2397188Smcpowers if ((ctr_ctx = calloc(1, sizeof (ctr_ctx_t))) == NULL) 2407188Smcpowers #endif 2417188Smcpowers return (NULL); 2427188Smcpowers 2437188Smcpowers ctr_ctx->ctr_flags = CTR_MODE; 2447188Smcpowers return (ctr_ctx); 2457188Smcpowers } 246