xref: /titanic_50/usr/src/common/crypto/modes/ctr.c (revision 4b56a00321e0ce508e55cc5e43e3ad7b00005a39)
123c57df7Smcpowers /*
223c57df7Smcpowers  * CDDL HEADER START
323c57df7Smcpowers  *
423c57df7Smcpowers  * The contents of this file are subject to the terms of the
523c57df7Smcpowers  * Common Development and Distribution License (the "License").
623c57df7Smcpowers  * You may not use this file except in compliance with the License.
723c57df7Smcpowers  *
823c57df7Smcpowers  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
923c57df7Smcpowers  * or http://www.opensolaris.org/os/licensing.
1023c57df7Smcpowers  * See the License for the specific language governing permissions
1123c57df7Smcpowers  * and limitations under the License.
1223c57df7Smcpowers  *
1323c57df7Smcpowers  * When distributing Covered Code, include this CDDL HEADER in each
1423c57df7Smcpowers  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1523c57df7Smcpowers  * If applicable, add the following below this CDDL HEADER, with the
1623c57df7Smcpowers  * fields enclosed by brackets "[]" replaced with your own identifying
1723c57df7Smcpowers  * information: Portions Copyright [yyyy] [name of copyright owner]
1823c57df7Smcpowers  *
1923c57df7Smcpowers  * CDDL HEADER END
2023c57df7Smcpowers  */
2123c57df7Smcpowers /*
2223c57df7Smcpowers  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2323c57df7Smcpowers  * Use is subject to license terms.
2423c57df7Smcpowers  */
2523c57df7Smcpowers 
2623c57df7Smcpowers #ifndef _KERNEL
2723c57df7Smcpowers #include <strings.h>
2823c57df7Smcpowers #include <limits.h>
2923c57df7Smcpowers #include <assert.h>
3023c57df7Smcpowers #include <security/cryptoki.h>
3123c57df7Smcpowers #endif
3223c57df7Smcpowers 
3323c57df7Smcpowers #include <sys/types.h>
3423c57df7Smcpowers #include <modes/modes.h>
3523c57df7Smcpowers #include <sys/crypto/common.h>
3623c57df7Smcpowers #include <sys/crypto/impl.h>
3723c57df7Smcpowers 
38*4b56a003SDaniel Anderson #ifdef _LITTLE_ENDIAN
39*4b56a003SDaniel Anderson #include <sys/byteorder.h>
40*4b56a003SDaniel Anderson #endif
41*4b56a003SDaniel Anderson 
4223c57df7Smcpowers /*
4323c57df7Smcpowers  * Encrypt and decrypt multiple blocks of data in counter mode.
4423c57df7Smcpowers  */
4523c57df7Smcpowers int
4623c57df7Smcpowers ctr_mode_contiguous_blocks(ctr_ctx_t *ctx, char *data, size_t length,
4723c57df7Smcpowers     crypto_data_t *out, size_t block_size,
4823c57df7Smcpowers     int (*cipher)(const void *ks, const uint8_t *pt, uint8_t *ct),
4923c57df7Smcpowers     void (*xor_block)(uint8_t *, uint8_t *))
5023c57df7Smcpowers {
5123c57df7Smcpowers 	size_t remainder = length;
5223c57df7Smcpowers 	size_t need;
5323c57df7Smcpowers 	uint8_t *datap = (uint8_t *)data;
5423c57df7Smcpowers 	uint8_t *blockp;
5523c57df7Smcpowers 	uint8_t *lastp;
5623c57df7Smcpowers 	void *iov_or_mp;
5723c57df7Smcpowers 	offset_t offset;
5823c57df7Smcpowers 	uint8_t *out_data_1;
5923c57df7Smcpowers 	uint8_t *out_data_2;
6023c57df7Smcpowers 	size_t out_data_1_len;
6123c57df7Smcpowers 	uint64_t counter;
6223c57df7Smcpowers #ifdef _LITTLE_ENDIAN
6323c57df7Smcpowers 	uint8_t *p;
6423c57df7Smcpowers #endif
6523c57df7Smcpowers 
6623c57df7Smcpowers 	if (length + ctx->ctr_remainder_len < block_size) {
6723c57df7Smcpowers 		/* accumulate bytes here and return */
6823c57df7Smcpowers 		bcopy(datap,
6923c57df7Smcpowers 		    (uint8_t *)ctx->ctr_remainder + ctx->ctr_remainder_len,
7023c57df7Smcpowers 		    length);
7123c57df7Smcpowers 		ctx->ctr_remainder_len += length;
7223c57df7Smcpowers 		ctx->ctr_copy_to = datap;
7323c57df7Smcpowers 		return (CRYPTO_SUCCESS);
7423c57df7Smcpowers 	}
7523c57df7Smcpowers 
7623c57df7Smcpowers 	lastp = (uint8_t *)ctx->ctr_cb;
7723c57df7Smcpowers 	if (out != NULL)
7823c57df7Smcpowers 		crypto_init_ptrs(out, &iov_or_mp, &offset);
7923c57df7Smcpowers 
8023c57df7Smcpowers 	do {
8123c57df7Smcpowers 		/* Unprocessed data from last call. */
8223c57df7Smcpowers 		if (ctx->ctr_remainder_len > 0) {
8323c57df7Smcpowers 			need = block_size - ctx->ctr_remainder_len;
8423c57df7Smcpowers 
8523c57df7Smcpowers 			if (need > remainder)
8623c57df7Smcpowers 				return (CRYPTO_DATA_LEN_RANGE);
8723c57df7Smcpowers 
8823c57df7Smcpowers 			bcopy(datap, &((uint8_t *)ctx->ctr_remainder)
8923c57df7Smcpowers 			    [ctx->ctr_remainder_len], need);
9023c57df7Smcpowers 
9123c57df7Smcpowers 			blockp = (uint8_t *)ctx->ctr_remainder;
9223c57df7Smcpowers 		} else {
9323c57df7Smcpowers 			blockp = datap;
9423c57df7Smcpowers 		}
9523c57df7Smcpowers 
9623c57df7Smcpowers 		/* ctr_cb is the counter block */
9723c57df7Smcpowers 		cipher(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb,
9823c57df7Smcpowers 		    (uint8_t *)ctx->ctr_tmp);
9923c57df7Smcpowers 
10023c57df7Smcpowers 		lastp = (uint8_t *)ctx->ctr_tmp;
10123c57df7Smcpowers 
10223c57df7Smcpowers 		/*
10323c57df7Smcpowers 		 * Increment counter. Counter bits are confined
10423c57df7Smcpowers 		 * to the bottom 64 bits of the counter block.
10523c57df7Smcpowers 		 */
106*4b56a003SDaniel Anderson #ifdef _LITTLE_ENDIAN
107*4b56a003SDaniel Anderson 		counter = ntohll(ctx->ctr_cb[1] & ctx->ctr_counter_mask);
108*4b56a003SDaniel Anderson 		counter = htonll(counter + 1);
109*4b56a003SDaniel Anderson #else
11023c57df7Smcpowers 		counter = ctx->ctr_cb[1] & ctx->ctr_counter_mask;
11123c57df7Smcpowers 		counter++;
112*4b56a003SDaniel Anderson #endif	/* _LITTLE_ENDIAN */
11323c57df7Smcpowers 		counter &= ctx->ctr_counter_mask;
11423c57df7Smcpowers 		ctx->ctr_cb[1] =
11523c57df7Smcpowers 		    (ctx->ctr_cb[1] & ~(ctx->ctr_counter_mask)) | counter;
11623c57df7Smcpowers 
11723c57df7Smcpowers 		/*
11823c57df7Smcpowers 		 * XOR the previous cipher block or IV with the
11923c57df7Smcpowers 		 * current clear block.
12023c57df7Smcpowers 		 */
12123c57df7Smcpowers 		xor_block(blockp, lastp);
12223c57df7Smcpowers 
12323c57df7Smcpowers 		if (out == NULL) {
12423c57df7Smcpowers 			if (ctx->ctr_remainder_len > 0) {
12523c57df7Smcpowers 				bcopy(lastp, ctx->ctr_copy_to,
12623c57df7Smcpowers 				    ctx->ctr_remainder_len);
12723c57df7Smcpowers 				bcopy(lastp + ctx->ctr_remainder_len, datap,
12823c57df7Smcpowers 				    need);
12923c57df7Smcpowers 			}
13023c57df7Smcpowers 		} else {
13123c57df7Smcpowers 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
13223c57df7Smcpowers 			    &out_data_1_len, &out_data_2, block_size);
13323c57df7Smcpowers 
13423c57df7Smcpowers 			/* copy block to where it belongs */
13523c57df7Smcpowers 			bcopy(lastp, out_data_1, out_data_1_len);
13623c57df7Smcpowers 			if (out_data_2 != NULL) {
13723c57df7Smcpowers 				bcopy(lastp + out_data_1_len, out_data_2,
13823c57df7Smcpowers 				    block_size - out_data_1_len);
13923c57df7Smcpowers 			}
14023c57df7Smcpowers 			/* update offset */
14123c57df7Smcpowers 			out->cd_offset += block_size;
14223c57df7Smcpowers 		}
14323c57df7Smcpowers 
14423c57df7Smcpowers 		/* Update pointer to next block of data to be processed. */
14523c57df7Smcpowers 		if (ctx->ctr_remainder_len != 0) {
14623c57df7Smcpowers 			datap += need;
14723c57df7Smcpowers 			ctx->ctr_remainder_len = 0;
14823c57df7Smcpowers 		} else {
14923c57df7Smcpowers 			datap += block_size;
15023c57df7Smcpowers 		}
15123c57df7Smcpowers 
15223c57df7Smcpowers 		remainder = (size_t)&data[length] - (size_t)datap;
15323c57df7Smcpowers 
15423c57df7Smcpowers 		/* Incomplete last block. */
15523c57df7Smcpowers 		if (remainder > 0 && remainder < block_size) {
15623c57df7Smcpowers 			bcopy(datap, ctx->ctr_remainder, remainder);
15723c57df7Smcpowers 			ctx->ctr_remainder_len = remainder;
15823c57df7Smcpowers 			ctx->ctr_copy_to = datap;
15923c57df7Smcpowers 			goto out;
16023c57df7Smcpowers 		}
16123c57df7Smcpowers 		ctx->ctr_copy_to = NULL;
16223c57df7Smcpowers 
16323c57df7Smcpowers 	} while (remainder > 0);
16423c57df7Smcpowers 
16523c57df7Smcpowers out:
16623c57df7Smcpowers 	return (CRYPTO_SUCCESS);
16723c57df7Smcpowers }
16823c57df7Smcpowers 
16923c57df7Smcpowers int
17023c57df7Smcpowers ctr_mode_final(ctr_ctx_t *ctx, crypto_data_t *out,
17123c57df7Smcpowers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *))
17223c57df7Smcpowers {
17323c57df7Smcpowers 	uint8_t *lastp;
17423c57df7Smcpowers 	void *iov_or_mp;
17523c57df7Smcpowers 	offset_t offset;
17623c57df7Smcpowers 	uint8_t *out_data_1;
17723c57df7Smcpowers 	uint8_t *out_data_2;
17823c57df7Smcpowers 	size_t out_data_1_len;
17923c57df7Smcpowers 	uint8_t *p;
18023c57df7Smcpowers 	int i;
18123c57df7Smcpowers 
18223c57df7Smcpowers 	if (out->cd_length < ctx->ctr_remainder_len)
18323c57df7Smcpowers 		return (CRYPTO_DATA_LEN_RANGE);
18423c57df7Smcpowers 
18523c57df7Smcpowers 	encrypt_block(ctx->ctr_keysched, (uint8_t *)ctx->ctr_cb,
18623c57df7Smcpowers 	    (uint8_t *)ctx->ctr_tmp);
18723c57df7Smcpowers 
18823c57df7Smcpowers 	lastp = (uint8_t *)ctx->ctr_tmp;
18923c57df7Smcpowers 	p = (uint8_t *)ctx->ctr_remainder;
19023c57df7Smcpowers 	for (i = 0; i < ctx->ctr_remainder_len; i++) {
19123c57df7Smcpowers 		p[i] ^= lastp[i];
19223c57df7Smcpowers 	}
19323c57df7Smcpowers 
19423c57df7Smcpowers 	crypto_init_ptrs(out, &iov_or_mp, &offset);
19523c57df7Smcpowers 	crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
19623c57df7Smcpowers 	    &out_data_1_len, &out_data_2, ctx->ctr_remainder_len);
19723c57df7Smcpowers 
19823c57df7Smcpowers 	bcopy(p, out_data_1, out_data_1_len);
19923c57df7Smcpowers 	if (out_data_2 != NULL) {
20023c57df7Smcpowers 		bcopy((uint8_t *)p + out_data_1_len,
20123c57df7Smcpowers 		    out_data_2, ctx->ctr_remainder_len - out_data_1_len);
20223c57df7Smcpowers 	}
20323c57df7Smcpowers 	out->cd_offset += ctx->ctr_remainder_len;
20423c57df7Smcpowers 	ctx->ctr_remainder_len = 0;
20523c57df7Smcpowers 	return (CRYPTO_SUCCESS);
20623c57df7Smcpowers }
20723c57df7Smcpowers 
20823c57df7Smcpowers int
20923c57df7Smcpowers ctr_init_ctx(ctr_ctx_t *ctr_ctx, ulong_t count, uint8_t *cb,
21023c57df7Smcpowers void (*copy_block)(uint8_t *, uint8_t *))
21123c57df7Smcpowers {
21223c57df7Smcpowers 	uint64_t mask = 0;
21323c57df7Smcpowers 
21423c57df7Smcpowers 	if (count == 0 || count > 64) {
21523c57df7Smcpowers 		return (CRYPTO_MECHANISM_PARAM_INVALID);
21623c57df7Smcpowers 	}
21723c57df7Smcpowers 	while (count-- > 0)
21823c57df7Smcpowers 		mask |= (1ULL << count);
219*4b56a003SDaniel Anderson 
22023c57df7Smcpowers #ifdef _LITTLE_ENDIAN
221*4b56a003SDaniel Anderson 	mask = htonll(mask);
22223c57df7Smcpowers #endif
22323c57df7Smcpowers 	ctr_ctx->ctr_counter_mask = mask;
22423c57df7Smcpowers 	copy_block(cb, (uchar_t *)ctr_ctx->ctr_cb);
22523c57df7Smcpowers 	ctr_ctx->ctr_lastp = (uint8_t *)&ctr_ctx->ctr_cb[0];
22623c57df7Smcpowers 	ctr_ctx->ctr_flags |= CTR_MODE;
22723c57df7Smcpowers 	return (CRYPTO_SUCCESS);
22823c57df7Smcpowers }
22923c57df7Smcpowers 
23023c57df7Smcpowers /* ARGSUSED */
23123c57df7Smcpowers void *
23223c57df7Smcpowers ctr_alloc_ctx(int kmflag)
23323c57df7Smcpowers {
23423c57df7Smcpowers 	ctr_ctx_t *ctr_ctx;
23523c57df7Smcpowers 
23623c57df7Smcpowers #ifdef _KERNEL
23723c57df7Smcpowers 	if ((ctr_ctx = kmem_zalloc(sizeof (ctr_ctx_t), kmflag)) == NULL)
23823c57df7Smcpowers #else
23923c57df7Smcpowers 	if ((ctr_ctx = calloc(1, sizeof (ctr_ctx_t))) == NULL)
24023c57df7Smcpowers #endif
24123c57df7Smcpowers 		return (NULL);
24223c57df7Smcpowers 
24323c57df7Smcpowers 	ctr_ctx->ctr_flags = CTR_MODE;
24423c57df7Smcpowers 	return (ctr_ctx);
24523c57df7Smcpowers }
246