xref: /illumos-gate/usr/src/common/crypto/modes/cbc.c (revision cd964fce)
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.
24*cd964fceSMatt Barden  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
2523c57df7Smcpowers  */
2623c57df7Smcpowers 
2723c57df7Smcpowers #ifndef _KERNEL
2823c57df7Smcpowers #include <strings.h>
2923c57df7Smcpowers #include <limits.h>
3023c57df7Smcpowers #include <assert.h>
3123c57df7Smcpowers #include <security/cryptoki.h>
3223c57df7Smcpowers #endif
3323c57df7Smcpowers 
34*cd964fceSMatt Barden #include <sys/debug.h>
3523c57df7Smcpowers #include <sys/types.h>
3623c57df7Smcpowers #include <modes/modes.h>
3723c57df7Smcpowers #include <sys/crypto/common.h>
3823c57df7Smcpowers #include <sys/crypto/impl.h>
39*cd964fceSMatt Barden #include <aes/aes_impl.h>
40*cd964fceSMatt Barden 
41*cd964fceSMatt Barden /* These are the CMAC Rb constants from NIST SP 800-38B */
42*cd964fceSMatt Barden #define	CONST_RB_128	0x87
43*cd964fceSMatt Barden #define	CONST_RB_64	0x1B
4423c57df7Smcpowers 
4523c57df7Smcpowers /*
4623c57df7Smcpowers  * Algorithm independent CBC functions.
4723c57df7Smcpowers  */
4823c57df7Smcpowers int
cbc_encrypt_contiguous_blocks(cbc_ctx_t * ctx,char * data,size_t length,crypto_data_t * out,size_t block_size,int (* encrypt)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))4923c57df7Smcpowers cbc_encrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
5023c57df7Smcpowers     crypto_data_t *out, size_t block_size,
5123c57df7Smcpowers     int (*encrypt)(const void *, const uint8_t *, uint8_t *),
5223c57df7Smcpowers     void (*copy_block)(uint8_t *, uint8_t *),
5323c57df7Smcpowers     void (*xor_block)(uint8_t *, uint8_t *))
5423c57df7Smcpowers {
5523c57df7Smcpowers 	size_t remainder = length;
5623c57df7Smcpowers 	size_t need;
5723c57df7Smcpowers 	uint8_t *datap = (uint8_t *)data;
5823c57df7Smcpowers 	uint8_t *blockp;
5923c57df7Smcpowers 	uint8_t *lastp;
6023c57df7Smcpowers 	void *iov_or_mp;
6123c57df7Smcpowers 	offset_t offset;
6223c57df7Smcpowers 	uint8_t *out_data_1;
6323c57df7Smcpowers 	uint8_t *out_data_2;
6423c57df7Smcpowers 	size_t out_data_1_len;
6523c57df7Smcpowers 
66*cd964fceSMatt Barden 	if (length + ctx->cbc_remainder_len < ctx->max_remain) {
6723c57df7Smcpowers 		/* accumulate bytes here and return */
6823c57df7Smcpowers 		bcopy(datap,
6916239bc8SMark Powers 		    (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
7023c57df7Smcpowers 		    length);
7116239bc8SMark Powers 		ctx->cbc_remainder_len += length;
7216239bc8SMark Powers 		ctx->cbc_copy_to = datap;
7323c57df7Smcpowers 		return (CRYPTO_SUCCESS);
7423c57df7Smcpowers 	}
7523c57df7Smcpowers 
7616239bc8SMark Powers 	lastp = (uint8_t *)ctx->cbc_iv;
7723c57df7Smcpowers 	if (out != NULL)
7823c57df7Smcpowers 		crypto_init_ptrs(out, &iov_or_mp, &offset);
7923c57df7Smcpowers 
8023c57df7Smcpowers 	do {
8123c57df7Smcpowers 		/* Unprocessed data from last call. */
8216239bc8SMark Powers 		if (ctx->cbc_remainder_len > 0) {
8316239bc8SMark Powers 			need = block_size - ctx->cbc_remainder_len;
8423c57df7Smcpowers 
8523c57df7Smcpowers 			if (need > remainder)
8623c57df7Smcpowers 				return (CRYPTO_DATA_LEN_RANGE);
8723c57df7Smcpowers 
8816239bc8SMark Powers 			bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
8916239bc8SMark Powers 			    [ctx->cbc_remainder_len], need);
9023c57df7Smcpowers 
9116239bc8SMark Powers 			blockp = (uint8_t *)ctx->cbc_remainder;
9223c57df7Smcpowers 		} else {
9323c57df7Smcpowers 			blockp = datap;
9423c57df7Smcpowers 		}
9523c57df7Smcpowers 
9623c57df7Smcpowers 		if (out == NULL) {
9723c57df7Smcpowers 			/*
9823c57df7Smcpowers 			 * XOR the previous cipher block or IV with the
9923c57df7Smcpowers 			 * current clear block.
10023c57df7Smcpowers 			 */
10123c57df7Smcpowers 			xor_block(lastp, blockp);
10216239bc8SMark Powers 			encrypt(ctx->cbc_keysched, blockp, blockp);
10323c57df7Smcpowers 
10416239bc8SMark Powers 			ctx->cbc_lastp = blockp;
10523c57df7Smcpowers 			lastp = blockp;
10623c57df7Smcpowers 
107*cd964fceSMatt Barden 			if ((ctx->cbc_flags & CMAC_MODE) == 0 &&
108*cd964fceSMatt Barden 			    ctx->cbc_remainder_len > 0) {
10916239bc8SMark Powers 				bcopy(blockp, ctx->cbc_copy_to,
11016239bc8SMark Powers 				    ctx->cbc_remainder_len);
11116239bc8SMark Powers 				bcopy(blockp + ctx->cbc_remainder_len, datap,
11223c57df7Smcpowers 				    need);
11323c57df7Smcpowers 			}
11423c57df7Smcpowers 		} else {
11523c57df7Smcpowers 			/*
11623c57df7Smcpowers 			 * XOR the previous cipher block or IV with the
11723c57df7Smcpowers 			 * current clear block.
11823c57df7Smcpowers 			 */
11923c57df7Smcpowers 			xor_block(blockp, lastp);
12016239bc8SMark Powers 			encrypt(ctx->cbc_keysched, lastp, lastp);
12123c57df7Smcpowers 
122*cd964fceSMatt Barden 			/*
123*cd964fceSMatt Barden 			 * CMAC doesn't output until encrypt_final
124*cd964fceSMatt Barden 			 */
125*cd964fceSMatt Barden 			if ((ctx->cbc_flags & CMAC_MODE) == 0) {
126*cd964fceSMatt Barden 				crypto_get_ptrs(out, &iov_or_mp, &offset,
127*cd964fceSMatt Barden 				    &out_data_1, &out_data_1_len,
128*cd964fceSMatt Barden 				    &out_data_2, block_size);
129*cd964fceSMatt Barden 
130*cd964fceSMatt Barden 				/* copy block to where it belongs */
131*cd964fceSMatt Barden 				if (out_data_1_len == block_size) {
132*cd964fceSMatt Barden 					copy_block(lastp, out_data_1);
133*cd964fceSMatt Barden 				} else {
134*cd964fceSMatt Barden 					bcopy(lastp, out_data_1,
135*cd964fceSMatt Barden 					    out_data_1_len);
136*cd964fceSMatt Barden 					if (out_data_2 != NULL) {
137*cd964fceSMatt Barden 						bcopy(lastp + out_data_1_len,
138*cd964fceSMatt Barden 						    out_data_2,
139*cd964fceSMatt Barden 						    block_size -
140*cd964fceSMatt Barden 						    out_data_1_len);
141*cd964fceSMatt Barden 					}
14223c57df7Smcpowers 				}
143*cd964fceSMatt Barden 				/* update offset */
144*cd964fceSMatt Barden 				out->cd_offset += block_size;
14523c57df7Smcpowers 			}
14623c57df7Smcpowers 		}
14723c57df7Smcpowers 
14823c57df7Smcpowers 		/* Update pointer to next block of data to be processed. */
14916239bc8SMark Powers 		if (ctx->cbc_remainder_len != 0) {
15023c57df7Smcpowers 			datap += need;
15116239bc8SMark Powers 			ctx->cbc_remainder_len = 0;
15223c57df7Smcpowers 		} else {
15323c57df7Smcpowers 			datap += block_size;
15423c57df7Smcpowers 		}
15523c57df7Smcpowers 
15623c57df7Smcpowers 		remainder = (size_t)&data[length] - (size_t)datap;
15723c57df7Smcpowers 
15823c57df7Smcpowers 		/* Incomplete last block. */
159*cd964fceSMatt Barden 		if (remainder > 0 && remainder < ctx->max_remain) {
16016239bc8SMark Powers 			bcopy(datap, ctx->cbc_remainder, remainder);
16116239bc8SMark Powers 			ctx->cbc_remainder_len = remainder;
16216239bc8SMark Powers 			ctx->cbc_copy_to = datap;
16323c57df7Smcpowers 			goto out;
16423c57df7Smcpowers 		}
16516239bc8SMark Powers 		ctx->cbc_copy_to = NULL;
16623c57df7Smcpowers 
16723c57df7Smcpowers 	} while (remainder > 0);
16823c57df7Smcpowers 
16923c57df7Smcpowers out:
17023c57df7Smcpowers 	/*
17123c57df7Smcpowers 	 * Save the last encrypted block in the context.
17223c57df7Smcpowers 	 */
17316239bc8SMark Powers 	if (ctx->cbc_lastp != NULL) {
17416239bc8SMark Powers 		copy_block((uint8_t *)ctx->cbc_lastp, (uint8_t *)ctx->cbc_iv);
17516239bc8SMark Powers 		ctx->cbc_lastp = (uint8_t *)ctx->cbc_iv;
17623c57df7Smcpowers 	}
17723c57df7Smcpowers 
17823c57df7Smcpowers 	return (CRYPTO_SUCCESS);
17923c57df7Smcpowers }
18023c57df7Smcpowers 
18123c57df7Smcpowers #define	OTHER(a, ctx) \
18216239bc8SMark Powers 	(((a) == (ctx)->cbc_lastblock) ? (ctx)->cbc_iv : (ctx)->cbc_lastblock)
18323c57df7Smcpowers 
18423c57df7Smcpowers /* ARGSUSED */
18523c57df7Smcpowers int
cbc_decrypt_contiguous_blocks(cbc_ctx_t * ctx,char * data,size_t length,crypto_data_t * out,size_t block_size,int (* decrypt)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))18623c57df7Smcpowers cbc_decrypt_contiguous_blocks(cbc_ctx_t *ctx, char *data, size_t length,
18723c57df7Smcpowers     crypto_data_t *out, size_t block_size,
18823c57df7Smcpowers     int (*decrypt)(const void *, const uint8_t *, uint8_t *),
18923c57df7Smcpowers     void (*copy_block)(uint8_t *, uint8_t *),
19023c57df7Smcpowers     void (*xor_block)(uint8_t *, uint8_t *))
19123c57df7Smcpowers {
19223c57df7Smcpowers 	size_t remainder = length;
19323c57df7Smcpowers 	size_t need;
19423c57df7Smcpowers 	uint8_t *datap = (uint8_t *)data;
19523c57df7Smcpowers 	uint8_t *blockp;
19623c57df7Smcpowers 	uint8_t *lastp;
19723c57df7Smcpowers 	void *iov_or_mp;
19823c57df7Smcpowers 	offset_t offset;
19923c57df7Smcpowers 	uint8_t *out_data_1;
20023c57df7Smcpowers 	uint8_t *out_data_2;
20123c57df7Smcpowers 	size_t out_data_1_len;
20223c57df7Smcpowers 
20316239bc8SMark Powers 	if (length + ctx->cbc_remainder_len < block_size) {
20423c57df7Smcpowers 		/* accumulate bytes here and return */
20523c57df7Smcpowers 		bcopy(datap,
20616239bc8SMark Powers 		    (uint8_t *)ctx->cbc_remainder + ctx->cbc_remainder_len,
20723c57df7Smcpowers 		    length);
20816239bc8SMark Powers 		ctx->cbc_remainder_len += length;
20916239bc8SMark Powers 		ctx->cbc_copy_to = datap;
21023c57df7Smcpowers 		return (CRYPTO_SUCCESS);
21123c57df7Smcpowers 	}
21223c57df7Smcpowers 
21316239bc8SMark Powers 	lastp = ctx->cbc_lastp;
21423c57df7Smcpowers 	if (out != NULL)
21523c57df7Smcpowers 		crypto_init_ptrs(out, &iov_or_mp, &offset);
21623c57df7Smcpowers 
21723c57df7Smcpowers 	do {
21823c57df7Smcpowers 		/* Unprocessed data from last call. */
21916239bc8SMark Powers 		if (ctx->cbc_remainder_len > 0) {
22016239bc8SMark Powers 			need = block_size - ctx->cbc_remainder_len;
22123c57df7Smcpowers 
22223c57df7Smcpowers 			if (need > remainder)
22323c57df7Smcpowers 				return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
22423c57df7Smcpowers 
22516239bc8SMark Powers 			bcopy(datap, &((uint8_t *)ctx->cbc_remainder)
22616239bc8SMark Powers 			    [ctx->cbc_remainder_len], need);
22723c57df7Smcpowers 
22816239bc8SMark Powers 			blockp = (uint8_t *)ctx->cbc_remainder;
22923c57df7Smcpowers 		} else {
23023c57df7Smcpowers 			blockp = datap;
23123c57df7Smcpowers 		}
23223c57df7Smcpowers 
23323c57df7Smcpowers 		/* LINTED: pointer alignment */
23423c57df7Smcpowers 		copy_block(blockp, (uint8_t *)OTHER((uint64_t *)lastp, ctx));
23523c57df7Smcpowers 
23623c57df7Smcpowers 		if (out != NULL) {
23716239bc8SMark Powers 			decrypt(ctx->cbc_keysched, blockp,
23816239bc8SMark Powers 			    (uint8_t *)ctx->cbc_remainder);
23916239bc8SMark Powers 			blockp = (uint8_t *)ctx->cbc_remainder;
24023c57df7Smcpowers 		} else {
24116239bc8SMark Powers 			decrypt(ctx->cbc_keysched, blockp, blockp);
24223c57df7Smcpowers 		}
24323c57df7Smcpowers 
24423c57df7Smcpowers 		/*
24523c57df7Smcpowers 		 * XOR the previous cipher block or IV with the
24623c57df7Smcpowers 		 * currently decrypted block.
24723c57df7Smcpowers 		 */
24823c57df7Smcpowers 		xor_block(lastp, blockp);
24923c57df7Smcpowers 
25023c57df7Smcpowers 		/* LINTED: pointer alignment */
25123c57df7Smcpowers 		lastp = (uint8_t *)OTHER((uint64_t *)lastp, ctx);
25223c57df7Smcpowers 
25323c57df7Smcpowers 		if (out != NULL) {
25423c57df7Smcpowers 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
25523c57df7Smcpowers 			    &out_data_1_len, &out_data_2, block_size);
25623c57df7Smcpowers 
25723c57df7Smcpowers 			bcopy(blockp, out_data_1, out_data_1_len);
25823c57df7Smcpowers 			if (out_data_2 != NULL) {
25923c57df7Smcpowers 				bcopy(blockp + out_data_1_len, out_data_2,
26023c57df7Smcpowers 				    block_size - out_data_1_len);
26123c57df7Smcpowers 			}
26223c57df7Smcpowers 
26323c57df7Smcpowers 			/* update offset */
26423c57df7Smcpowers 			out->cd_offset += block_size;
26523c57df7Smcpowers 
26616239bc8SMark Powers 		} else if (ctx->cbc_remainder_len > 0) {
26723c57df7Smcpowers 			/* copy temporary block to where it belongs */
26816239bc8SMark Powers 			bcopy(blockp, ctx->cbc_copy_to, ctx->cbc_remainder_len);
26916239bc8SMark Powers 			bcopy(blockp + ctx->cbc_remainder_len, datap, need);
27023c57df7Smcpowers 		}
27123c57df7Smcpowers 
27223c57df7Smcpowers 		/* Update pointer to next block of data to be processed. */
27316239bc8SMark Powers 		if (ctx->cbc_remainder_len != 0) {
27423c57df7Smcpowers 			datap += need;
27516239bc8SMark Powers 			ctx->cbc_remainder_len = 0;
27623c57df7Smcpowers 		} else {
27723c57df7Smcpowers 			datap += block_size;
27823c57df7Smcpowers 		}
27923c57df7Smcpowers 
28023c57df7Smcpowers 		remainder = (size_t)&data[length] - (size_t)datap;
28123c57df7Smcpowers 
28223c57df7Smcpowers 		/* Incomplete last block. */
28323c57df7Smcpowers 		if (remainder > 0 && remainder < block_size) {
28416239bc8SMark Powers 			bcopy(datap, ctx->cbc_remainder, remainder);
28516239bc8SMark Powers 			ctx->cbc_remainder_len = remainder;
28616239bc8SMark Powers 			ctx->cbc_lastp = lastp;
28716239bc8SMark Powers 			ctx->cbc_copy_to = datap;
28823c57df7Smcpowers 			return (CRYPTO_SUCCESS);
28923c57df7Smcpowers 		}
29016239bc8SMark Powers 		ctx->cbc_copy_to = NULL;
29123c57df7Smcpowers 
29223c57df7Smcpowers 	} while (remainder > 0);
29323c57df7Smcpowers 
29416239bc8SMark Powers 	ctx->cbc_lastp = lastp;
29523c57df7Smcpowers 	return (CRYPTO_SUCCESS);
29623c57df7Smcpowers }
29723c57df7Smcpowers 
29823c57df7Smcpowers int
cbc_init_ctx(cbc_ctx_t * cbc_ctx,char * param,size_t param_len,size_t block_size,void (* copy_block)(uint8_t *,uint64_t *))29923c57df7Smcpowers cbc_init_ctx(cbc_ctx_t *cbc_ctx, char *param, size_t param_len,
30023c57df7Smcpowers     size_t block_size, void (*copy_block)(uint8_t *, uint64_t *))
30123c57df7Smcpowers {
30223c57df7Smcpowers 	/*
30323c57df7Smcpowers 	 * Copy IV into context.
30423c57df7Smcpowers 	 *
30523c57df7Smcpowers 	 * If cm_param == NULL then the IV comes from the
30623c57df7Smcpowers 	 * cd_miscdata field in the crypto_data structure.
30723c57df7Smcpowers 	 */
30823c57df7Smcpowers 	if (param != NULL) {
30923c57df7Smcpowers #ifdef _KERNEL
31023c57df7Smcpowers 		ASSERT(param_len == block_size);
31123c57df7Smcpowers #else
31223c57df7Smcpowers 		assert(param_len == block_size);
31323c57df7Smcpowers #endif
31416239bc8SMark Powers 		copy_block((uchar_t *)param, cbc_ctx->cbc_iv);
31523c57df7Smcpowers 	}
31623c57df7Smcpowers 
31716239bc8SMark Powers 	cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0];
31816239bc8SMark Powers 	cbc_ctx->cbc_flags |= CBC_MODE;
319*cd964fceSMatt Barden 	cbc_ctx->max_remain = block_size;
32023c57df7Smcpowers 	return (CRYPTO_SUCCESS);
32123c57df7Smcpowers }
32223c57df7Smcpowers 
32323c57df7Smcpowers /* ARGSUSED */
324*cd964fceSMatt Barden static void *
cbc_cmac_alloc_ctx(int kmflag,uint32_t mode)325*cd964fceSMatt Barden cbc_cmac_alloc_ctx(int kmflag, uint32_t mode)
32623c57df7Smcpowers {
32723c57df7Smcpowers 	cbc_ctx_t *cbc_ctx;
328*cd964fceSMatt Barden 	uint32_t modeval = mode & (CBC_MODE|CMAC_MODE);
329*cd964fceSMatt Barden 
330*cd964fceSMatt Barden 	/* Only one of the two modes can be set */
331*cd964fceSMatt Barden 	VERIFY(modeval == CBC_MODE || modeval == CMAC_MODE);
33223c57df7Smcpowers 
33323c57df7Smcpowers #ifdef _KERNEL
33423c57df7Smcpowers 	if ((cbc_ctx = kmem_zalloc(sizeof (cbc_ctx_t), kmflag)) == NULL)
33523c57df7Smcpowers #else
33623c57df7Smcpowers 	if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
33723c57df7Smcpowers #endif
33823c57df7Smcpowers 		return (NULL);
33923c57df7Smcpowers 
340*cd964fceSMatt Barden 	cbc_ctx->cbc_flags = mode;
34123c57df7Smcpowers 	return (cbc_ctx);
34223c57df7Smcpowers }
343*cd964fceSMatt Barden 
344*cd964fceSMatt Barden void *
cbc_alloc_ctx(int kmflag)345*cd964fceSMatt Barden cbc_alloc_ctx(int kmflag)
346*cd964fceSMatt Barden {
347*cd964fceSMatt Barden 	return (cbc_cmac_alloc_ctx(kmflag, CBC_MODE));
348*cd964fceSMatt Barden }
349*cd964fceSMatt Barden 
350*cd964fceSMatt Barden /*
351*cd964fceSMatt Barden  * Algorithms for supporting AES-CMAC
352*cd964fceSMatt Barden  * NOTE: CMAC is generally just a wrapper for CBC
353*cd964fceSMatt Barden  */
354*cd964fceSMatt Barden 
355*cd964fceSMatt Barden void *
cmac_alloc_ctx(int kmflag)356*cd964fceSMatt Barden cmac_alloc_ctx(int kmflag)
357*cd964fceSMatt Barden {
358*cd964fceSMatt Barden 	return (cbc_cmac_alloc_ctx(kmflag, CMAC_MODE));
359*cd964fceSMatt Barden }
360*cd964fceSMatt Barden 
361*cd964fceSMatt Barden 
362*cd964fceSMatt Barden /*
363*cd964fceSMatt Barden  * Typically max_remain is set to block_size - 1, since we usually
364*cd964fceSMatt Barden  * will process the data once we have a full block.  However with CMAC,
365*cd964fceSMatt Barden  * we must preprocess the final block of data.  Since we cannot know
366*cd964fceSMatt Barden  * when we've received the final block of data until the _final() method
367*cd964fceSMatt Barden  * is called, we must not process the last block of data until we know
368*cd964fceSMatt Barden  * it is the last block, or we receive a new block of data.  As such,
369*cd964fceSMatt Barden  * max_remain for CMAC is block_size + 1.
370*cd964fceSMatt Barden  */
371*cd964fceSMatt Barden int
cmac_init_ctx(cbc_ctx_t * cbc_ctx,size_t block_size)372*cd964fceSMatt Barden cmac_init_ctx(cbc_ctx_t *cbc_ctx, size_t block_size)
373*cd964fceSMatt Barden {
374*cd964fceSMatt Barden 	/*
375*cd964fceSMatt Barden 	 * CMAC is only approved for block sizes 64 and 128 bits /
376*cd964fceSMatt Barden 	 * 8 and 16 bytes.
377*cd964fceSMatt Barden 	 */
378*cd964fceSMatt Barden 
379*cd964fceSMatt Barden 	if (block_size != 16 && block_size != 8)
380*cd964fceSMatt Barden 		return (CRYPTO_INVALID_CONTEXT);
381*cd964fceSMatt Barden 
382*cd964fceSMatt Barden 	/*
383*cd964fceSMatt Barden 	 * For CMAC, cbc_iv is always 0.
384*cd964fceSMatt Barden 	 */
385*cd964fceSMatt Barden 
386*cd964fceSMatt Barden 	cbc_ctx->cbc_iv[0] = 0;
387*cd964fceSMatt Barden 	cbc_ctx->cbc_iv[1] = 0;
388*cd964fceSMatt Barden 
389*cd964fceSMatt Barden 	cbc_ctx->cbc_lastp = (uint8_t *)&cbc_ctx->cbc_iv[0];
390*cd964fceSMatt Barden 	cbc_ctx->cbc_flags |= CMAC_MODE;
391*cd964fceSMatt Barden 
392*cd964fceSMatt Barden 	cbc_ctx->max_remain = block_size + 1;
393*cd964fceSMatt Barden 	return (CRYPTO_SUCCESS);
394*cd964fceSMatt Barden }
395*cd964fceSMatt Barden 
396*cd964fceSMatt Barden /*
397*cd964fceSMatt Barden  * Left shifts blocks by one and returns the leftmost bit
398*cd964fceSMatt Barden  */
399*cd964fceSMatt Barden static uint8_t
cmac_left_shift_block_by1(uint8_t * block,size_t block_size)400*cd964fceSMatt Barden cmac_left_shift_block_by1(uint8_t *block, size_t block_size)
401*cd964fceSMatt Barden {
402*cd964fceSMatt Barden 	uint8_t carry = 0, old;
403*cd964fceSMatt Barden 	size_t i;
404*cd964fceSMatt Barden 	for (i = block_size; i > 0; i--) {
405*cd964fceSMatt Barden 		old = carry;
406*cd964fceSMatt Barden 		carry = (block[i - 1] & 0x80) ? 1 : 0;
407*cd964fceSMatt Barden 		block[i - 1] = (block[i - 1] << 1) | old;
408*cd964fceSMatt Barden 	}
409*cd964fceSMatt Barden 	return (carry);
410*cd964fceSMatt Barden }
411*cd964fceSMatt Barden 
412*cd964fceSMatt Barden /*
413*cd964fceSMatt Barden  * Generate subkeys to preprocess the last block according to RFC 4493.
414*cd964fceSMatt Barden  * Store the final block_size MAC generated in 'out'.
415*cd964fceSMatt Barden  */
416*cd964fceSMatt Barden int
cmac_mode_final(cbc_ctx_t * cbc_ctx,crypto_data_t * out,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))417*cd964fceSMatt Barden cmac_mode_final(cbc_ctx_t *cbc_ctx, crypto_data_t *out,
418*cd964fceSMatt Barden     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
419*cd964fceSMatt Barden     void (*xor_block)(uint8_t *, uint8_t *))
420*cd964fceSMatt Barden {
421*cd964fceSMatt Barden 	uint8_t buf[AES_BLOCK_LEN] = {0};
422*cd964fceSMatt Barden 	uint8_t *M_last = (uint8_t *)cbc_ctx->cbc_remainder;
423*cd964fceSMatt Barden 	size_t length = cbc_ctx->cbc_remainder_len;
424*cd964fceSMatt Barden 	size_t block_size = cbc_ctx->max_remain - 1;
425*cd964fceSMatt Barden 	uint8_t const_rb;
426*cd964fceSMatt Barden 
427*cd964fceSMatt Barden 	if (length > block_size)
428*cd964fceSMatt Barden 		return (CRYPTO_INVALID_CONTEXT);
429*cd964fceSMatt Barden 
430*cd964fceSMatt Barden 	if (out->cd_length < block_size)
431*cd964fceSMatt Barden 		return (CRYPTO_DATA_LEN_RANGE);
432*cd964fceSMatt Barden 
433*cd964fceSMatt Barden 	if (block_size == 16)
434*cd964fceSMatt Barden 		const_rb = CONST_RB_128;
435*cd964fceSMatt Barden 	else if (block_size == 8)
436*cd964fceSMatt Barden 		const_rb = CONST_RB_64;
437*cd964fceSMatt Barden 	else
438*cd964fceSMatt Barden 		return (CRYPTO_INVALID_CONTEXT);
439*cd964fceSMatt Barden 
440*cd964fceSMatt Barden 	/* k_0 = E_k(0) */
441*cd964fceSMatt Barden 	encrypt_block(cbc_ctx->cbc_keysched, buf, buf);
442*cd964fceSMatt Barden 
443*cd964fceSMatt Barden 	if (cmac_left_shift_block_by1(buf, block_size))
444*cd964fceSMatt Barden 		buf[block_size - 1] ^= const_rb;
445*cd964fceSMatt Barden 
446*cd964fceSMatt Barden 	if (length == block_size) {
447*cd964fceSMatt Barden 		/* Last block complete, so m_n = k_1 + m_n' */
448*cd964fceSMatt Barden 		xor_block(buf, M_last);
449*cd964fceSMatt Barden 		xor_block(cbc_ctx->cbc_lastp, M_last);
450*cd964fceSMatt Barden 		encrypt_block(cbc_ctx->cbc_keysched, M_last, M_last);
451*cd964fceSMatt Barden 	} else {
452*cd964fceSMatt Barden 		/* Last block incomplete, so m_n = k_2 + (m_n' | 100...0_bin) */
453*cd964fceSMatt Barden 		if (cmac_left_shift_block_by1(buf, block_size))
454*cd964fceSMatt Barden 			buf[block_size - 1] ^= const_rb;
455*cd964fceSMatt Barden 
456*cd964fceSMatt Barden 		M_last[length] = 0x80;
457*cd964fceSMatt Barden 		bzero(M_last + length + 1, block_size - length - 1);
458*cd964fceSMatt Barden 		xor_block(buf, M_last);
459*cd964fceSMatt Barden 		xor_block(cbc_ctx->cbc_lastp, M_last);
460*cd964fceSMatt Barden 		encrypt_block(cbc_ctx->cbc_keysched, M_last, M_last);
461*cd964fceSMatt Barden 	}
462*cd964fceSMatt Barden 
463*cd964fceSMatt Barden 	/*
464*cd964fceSMatt Barden 	 * zero out the sub-key.
465*cd964fceSMatt Barden 	 */
466*cd964fceSMatt Barden #ifndef _KERNEL
467*cd964fceSMatt Barden 	explicit_bzero(&buf, sizeof (buf));
468*cd964fceSMatt Barden #else
469*cd964fceSMatt Barden 	bzero(&buf, sizeof (buf));
470*cd964fceSMatt Barden #endif
471*cd964fceSMatt Barden 	return (crypto_put_output_data(M_last, out, block_size));
472*cd964fceSMatt Barden }
473