xref: /titanic_51/usr/src/common/crypto/modes/cbc.c (revision 16239bc82c111618343e0a5b1a70e0fc702d00e0)
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 
3823c57df7Smcpowers /*
3923c57df7Smcpowers  * Algorithm independent CBC functions.
4023c57df7Smcpowers  */
4123c57df7Smcpowers int
4223c57df7Smcpowers cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
4323c57df7Smcpowers     crypto_data_t *out, size_t block_size,
4423c57df7Smcpowers     int (*encrypt)(const void *, const uint8_t *, uint8_t *),
4523c57df7Smcpowers     void (*copy_block)(uint8_t *, uint8_t *),
4623c57df7Smcpowers     void (*xor_block)(uint8_t *, uint8_t *))
4723c57df7Smcpowers {
4823c57df7Smcpowers 	size_t remainder = length;
4923c57df7Smcpowers 	size_t need;
5023c57df7Smcpowers 	uint8_t *datap = (uint8_t *)data;
5123c57df7Smcpowers 	uint8_t *blockp;
5223c57df7Smcpowers 	uint8_t *lastp;
5323c57df7Smcpowers 	void *iov_or_mp;
5423c57df7Smcpowers 	offset_t offset;
5523c57df7Smcpowers 	uint8_t *out_data_1;
5623c57df7Smcpowers 	uint8_t *out_data_2;
5723c57df7Smcpowers 	size_t out_data_1_len;
5823c57df7Smcpowers 
59*16239bc8SMark Powers 	if (length + ctx->cbc_remainder_len < block_size) {
6023c57df7Smcpowers 		/* accumulate bytes here and return */
6123c57df7Smcpowers 		bcopy(datap,
62*16239bc8SMark Powers 		    (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
6323c57df7Smcpowers 		    length);
64*16239bc8SMark Powers 		ctx->cbc_remainder_len += length;
65*16239bc8SMark Powers 		ctx->cbc_copy_to = datap;
6623c57df7Smcpowers 		return (CRYPTO_SUCCESS);
6723c57df7Smcpowers 	}
6823c57df7Smcpowers 
69*16239bc8SMark Powers 	lastp = (uint8_t *)ctx->cbc_iv;
7023c57df7Smcpowers 	if (out != NULL)
7123c57df7Smcpowers 		crypto_init_ptrs(out, &iov_or_mp, &offset);
7223c57df7Smcpowers 
7323c57df7Smcpowers 	do {
7423c57df7Smcpowers 		/* Unprocessed data from last call. */
75*16239bc8SMark Powers 		if (ctx->cbc_remainder_len > 0) {
76*16239bc8SMark Powers 			need = block_size - ctx->cbc_remainder_len;
7723c57df7Smcpowers 
7823c57df7Smcpowers 			if (need > remainder)
7923c57df7Smcpowers 				return (CRYPTO_DATA_LEN_RANGE);
8023c57df7Smcpowers 
81*16239bc8SMark Powers 			bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
82*16239bc8SMark Powers 			    [ctx->cbc_remainder_len], need);
8323c57df7Smcpowers 
84*16239bc8SMark Powers 			blockp = (uint8_t *)ctx->cbc_remainder;
8523c57df7Smcpowers 		} else {
8623c57df7Smcpowers 			blockp = datap;
8723c57df7Smcpowers 		}
8823c57df7Smcpowers 
8923c57df7Smcpowers 		if (out == NULL) {
9023c57df7Smcpowers 			/*
9123c57df7Smcpowers 			 * XOR the previous cipher block or IV with the
9223c57df7Smcpowers 			 * current clear block.
9323c57df7Smcpowers 			 */
9423c57df7Smcpowers 			xor_block(lastp, blockp);
95*16239bc8SMark Powers 			encrypt(ctx->cbc_keysched, blockp, blockp);
9623c57df7Smcpowers 
97*16239bc8SMark Powers 			ctx->cbc_lastp = blockp;
9823c57df7Smcpowers 			lastp = blockp;
9923c57df7Smcpowers 
100*16239bc8SMark Powers 			if (ctx->cbc_remainder_len > 0) {
101*16239bc8SMark Powers 				bcopy(blockp, ctx->cbc_copy_to,
102*16239bc8SMark Powers 				    ctx->cbc_remainder_len);
103*16239bc8SMark Powers 				bcopy(blockp + ctx->cbc_remainder_len, datap,
10423c57df7Smcpowers 				    need);
10523c57df7Smcpowers 			}
10623c57df7Smcpowers 		} else {
10723c57df7Smcpowers 			/*
10823c57df7Smcpowers 			 * XOR the previous cipher block or IV with the
10923c57df7Smcpowers 			 * current clear block.
11023c57df7Smcpowers 			 */
11123c57df7Smcpowers 			xor_block(blockp, lastp);
112*16239bc8SMark Powers 			encrypt(ctx->cbc_keysched, lastp, lastp);
11323c57df7Smcpowers 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
11423c57df7Smcpowers 			    &out_data_1_len, &out_data_2, block_size);
11523c57df7Smcpowers 
11623c57df7Smcpowers 			/* copy block to where it belongs */
11723c57df7Smcpowers 			if (out_data_1_len == block_size) {
11823c57df7Smcpowers 				copy_block(lastp, out_data_1);
11923c57df7Smcpowers 			} else {
12023c57df7Smcpowers 				bcopy(lastp, out_data_1, out_data_1_len);
12123c57df7Smcpowers 				if (out_data_2 != NULL) {
12223c57df7Smcpowers 					bcopy(lastp + out_data_1_len,
12323c57df7Smcpowers 					    out_data_2,
12423c57df7Smcpowers 					    block_size - out_data_1_len);
12523c57df7Smcpowers 				}
12623c57df7Smcpowers 			}
12723c57df7Smcpowers 			/* update offset */
12823c57df7Smcpowers 			out->cd_offset += block_size;
12923c57df7Smcpowers 		}
13023c57df7Smcpowers 
13123c57df7Smcpowers 		/* Update pointer to next block of data to be processed. */
132*16239bc8SMark Powers 		if (ctx->cbc_remainder_len != 0) {
13323c57df7Smcpowers 			datap += need;
134*16239bc8SMark Powers 			ctx->cbc_remainder_len = 0;
13523c57df7Smcpowers 		} else {
13623c57df7Smcpowers 			datap += block_size;
13723c57df7Smcpowers 		}
13823c57df7Smcpowers 
13923c57df7Smcpowers 		remainder = (size_t)&data[length] - (size_t)datap;
14023c57df7Smcpowers 
14123c57df7Smcpowers 		/* Incomplete last block. */
14223c57df7Smcpowers 		if (remainder > 0 && remainder < block_size) {
143*16239bc8SMark Powers 			bcopy(datap, ctx->cbc_remainder, remainder);
144*16239bc8SMark Powers 			ctx->cbc_remainder_len = remainder;
145*16239bc8SMark Powers 			ctx->cbc_copy_to = datap;
14623c57df7Smcpowers 			goto out;
14723c57df7Smcpowers 		}
148*16239bc8SMark Powers 		ctx->cbc_copy_to = NULL;
14923c57df7Smcpowers 
15023c57df7Smcpowers 	} while (remainder > 0);
15123c57df7Smcpowers 
15223c57df7Smcpowers out:
15323c57df7Smcpowers 	/*
15423c57df7Smcpowers 	 * Save the last encrypted block in the context.
15523c57df7Smcpowers 	 */
156*16239bc8SMark Powers 	if (ctx->cbc_lastp != NULL) {
157*16239bc8SMark Powers 		copy_block((uint8_t *)ctx->cbc_lastp, (uint8_t *)ctx->cbc_iv);
158*16239bc8SMark Powers 		ctx->cbc_lastp = (uint8_t *)ctx->cbc_iv;
15923c57df7Smcpowers 	}
16023c57df7Smcpowers 
16123c57df7Smcpowers 	return (CRYPTO_SUCCESS);
16223c57df7Smcpowers }
16323c57df7Smcpowers 
16423c57df7Smcpowers #define	OTHER(a, ctx) \
165*16239bc8SMark Powers 	(((a) == (ctx)->cbc_lastblock) ? (ctx)->cbc_iv : (ctx)->cbc_lastblock)
16623c57df7Smcpowers 
16723c57df7Smcpowers /* ARGSUSED */
16823c57df7Smcpowers int
16923c57df7Smcpowers cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
17023c57df7Smcpowers     crypto_data_t *out, size_t block_size,
17123c57df7Smcpowers     int (*decrypt)(const void *, const uint8_t *, uint8_t *),
17223c57df7Smcpowers     void (*copy_block)(uint8_t *, uint8_t *),
17323c57df7Smcpowers     void (*xor_block)(uint8_t *, uint8_t *))
17423c57df7Smcpowers {
17523c57df7Smcpowers 	size_t remainder = length;
17623c57df7Smcpowers 	size_t need;
17723c57df7Smcpowers 	uint8_t *datap = (uint8_t *)data;
17823c57df7Smcpowers 	uint8_t *blockp;
17923c57df7Smcpowers 	uint8_t *lastp;
18023c57df7Smcpowers 	void *iov_or_mp;
18123c57df7Smcpowers 	offset_t offset;
18223c57df7Smcpowers 	uint8_t *out_data_1;
18323c57df7Smcpowers 	uint8_t *out_data_2;
18423c57df7Smcpowers 	size_t out_data_1_len;
18523c57df7Smcpowers 
186*16239bc8SMark Powers 	if (length + ctx->cbc_remainder_len < block_size) {
18723c57df7Smcpowers 		/* accumulate bytes here and return */
18823c57df7Smcpowers 		bcopy(datap,
189*16239bc8SMark Powers 		    (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
19023c57df7Smcpowers 		    length);
191*16239bc8SMark Powers 		ctx->cbc_remainder_len += length;
192*16239bc8SMark Powers 		ctx->cbc_copy_to = datap;
19323c57df7Smcpowers 		return (CRYPTO_SUCCESS);
19423c57df7Smcpowers 	}
19523c57df7Smcpowers 
196*16239bc8SMark Powers 	lastp = ctx->cbc_lastp;
19723c57df7Smcpowers 	if (out != NULL)
19823c57df7Smcpowers 		crypto_init_ptrs(out, &iov_or_mp, &offset);
19923c57df7Smcpowers 
20023c57df7Smcpowers 	do {
20123c57df7Smcpowers 		/* Unprocessed data from last call. */
202*16239bc8SMark Powers 		if (ctx->cbc_remainder_len > 0) {
203*16239bc8SMark Powers 			need = block_size - ctx->cbc_remainder_len;
20423c57df7Smcpowers 
20523c57df7Smcpowers 			if (need > remainder)
20623c57df7Smcpowers 				return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
20723c57df7Smcpowers 
208*16239bc8SMark Powers 			bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
209*16239bc8SMark Powers 			    [ctx->cbc_remainder_len], need);
21023c57df7Smcpowers 
211*16239bc8SMark Powers 			blockp = (uint8_t *)ctx->cbc_remainder;
21223c57df7Smcpowers 		} else {
21323c57df7Smcpowers 			blockp = datap;
21423c57df7Smcpowers 		}
21523c57df7Smcpowers 
21623c57df7Smcpowers 		/* LINTED: pointer alignment */
21723c57df7Smcpowers 		copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx));
21823c57df7Smcpowers 
21923c57df7Smcpowers 		if (out != NULL) {
220*16239bc8SMark Powers 			decrypt(ctx->cbc_keysched, blockp,
221*16239bc8SMark Powers 			    (uint8_t *)ctx->cbc_remainder);
222*16239bc8SMark Powers 			blockp = (uint8_t *)ctx->cbc_remainder;
22323c57df7Smcpowers 		} else {
224*16239bc8SMark Powers 			decrypt(ctx->cbc_keysched, blockp, blockp);
22523c57df7Smcpowers 		}
22623c57df7Smcpowers 
22723c57df7Smcpowers 		/*
22823c57df7Smcpowers 		 * XOR the previous cipher block or IV with the
22923c57df7Smcpowers 		 * currently decrypted block.
23023c57df7Smcpowers 		 */
23123c57df7Smcpowers 		xor_block(lastp, blockp);
23223c57df7Smcpowers 
23323c57df7Smcpowers 		/* LINTED: pointer alignment */
23423c57df7Smcpowers 		lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx);
23523c57df7Smcpowers 
23623c57df7Smcpowers 		if (out != NULL) {
23723c57df7Smcpowers 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
23823c57df7Smcpowers 			    &out_data_1_len, &out_data_2, block_size);
23923c57df7Smcpowers 
24023c57df7Smcpowers 			bcopy(blockp, out_data_1, out_data_1_len);
24123c57df7Smcpowers 			if (out_data_2 != NULL) {
24223c57df7Smcpowers 				bcopy(blockp + out_data_1_len, out_data_2,
24323c57df7Smcpowers 				    block_size - out_data_1_len);
24423c57df7Smcpowers 			}
24523c57df7Smcpowers 
24623c57df7Smcpowers 			/* update offset */
24723c57df7Smcpowers 			out->cd_offset += block_size;
24823c57df7Smcpowers 
249*16239bc8SMark Powers 		} else if (ctx->cbc_remainder_len > 0) {
25023c57df7Smcpowers 			/* copy temporary block to where it belongs */
251*16239bc8SMark Powers 			bcopy(blockp, ctx->cbc_copy_to, ctx->cbc_remainder_len);
252*16239bc8SMark Powers 			bcopy(blockp + ctx->cbc_remainder_len, datap, need);
25323c57df7Smcpowers 		}
25423c57df7Smcpowers 
25523c57df7Smcpowers 		/* Update pointer to next block of data to be processed. */
256*16239bc8SMark Powers 		if (ctx->cbc_remainder_len != 0) {
25723c57df7Smcpowers 			datap += need;
258*16239bc8SMark Powers 			ctx->cbc_remainder_len = 0;
25923c57df7Smcpowers 		} else {
26023c57df7Smcpowers 			datap += block_size;
26123c57df7Smcpowers 		}
26223c57df7Smcpowers 
26323c57df7Smcpowers 		remainder = (size_t)&data[length] - (size_t)datap;
26423c57df7Smcpowers 
26523c57df7Smcpowers 		/* Incomplete last block. */
26623c57df7Smcpowers 		if (remainder > 0 && remainder < block_size) {
267*16239bc8SMark Powers 			bcopy(datap, ctx->cbc_remainder, remainder);
268*16239bc8SMark Powers 			ctx->cbc_remainder_len = remainder;
269*16239bc8SMark Powers 			ctx->cbc_lastp = lastp;
270*16239bc8SMark Powers 			ctx->cbc_copy_to = datap;
27123c57df7Smcpowers 			return (CRYPTO_SUCCESS);
27223c57df7Smcpowers 		}
273*16239bc8SMark Powers 		ctx->cbc_copy_to = NULL;
27423c57df7Smcpowers 
27523c57df7Smcpowers 	} while (remainder > 0);
27623c57df7Smcpowers 
277*16239bc8SMark Powers 	ctx->cbc_lastp = lastp;
27823c57df7Smcpowers 	return (CRYPTO_SUCCESS);
27923c57df7Smcpowers }
28023c57df7Smcpowers 
28123c57df7Smcpowers int
28223c57df7Smcpowers cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len,
28323c57df7Smcpowers     size_t block_size, void (*copy_block)(uint8_t *, uint64_t *))
28423c57df7Smcpowers {
28523c57df7Smcpowers 	/*
28623c57df7Smcpowers 	 * Copy IV into context.
28723c57df7Smcpowers 	 *
28823c57df7Smcpowers 	 * If cm_param == NULL then the IV comes from the
28923c57df7Smcpowers 	 * cd_miscdata field in the crypto_data structure.
29023c57df7Smcpowers 	 */
29123c57df7Smcpowers 	if (param != NULL) {
29223c57df7Smcpowers #ifdef _KERNEL
29323c57df7Smcpowers 		ASSERT(param_len == block_size);
29423c57df7Smcpowers #else
29523c57df7Smcpowers 		assert(param_len == block_size);
29623c57df7Smcpowers #endif
297*16239bc8SMark Powers 		copy_block((uchar_t *)param, cbc_ctx->cbc_iv);
29823c57df7Smcpowers 	}
29923c57df7Smcpowers 
300*16239bc8SMark Powers 	cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0];
301*16239bc8SMark Powers 	cbc_ctx->cbc_flags |= CBC_MODE;
30223c57df7Smcpowers 	return (CRYPTO_SUCCESS);
30323c57df7Smcpowers }
30423c57df7Smcpowers 
30523c57df7Smcpowers /* ARGSUSED */
30623c57df7Smcpowers void *
30723c57df7Smcpowers cbc_alloc_ctx(int kmflag)
30823c57df7Smcpowers {
30923c57df7Smcpowers 	cbc_ctx_t *cbc_ctx;
31023c57df7Smcpowers 
31123c57df7Smcpowers #ifdef _KERNEL
31223c57df7Smcpowers 	if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL)
31323c57df7Smcpowers #else
31423c57df7Smcpowers 	if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
31523c57df7Smcpowers #endif
31623c57df7Smcpowers 		return (NULL);
31723c57df7Smcpowers 
318*16239bc8SMark Powers 	cbc_ctx->cbc_flags = CBC_MODE;
31923c57df7Smcpowers 	return (cbc_ctx);
32023c57df7Smcpowers }
321