xref: /onnv-gate/usr/src/common/crypto/modes/gcm.c (revision 12826:fca99d9e3f2f)
18005SMark.Powers@Sun.COM /*
28005SMark.Powers@Sun.COM  * CDDL HEADER START
38005SMark.Powers@Sun.COM  *
48005SMark.Powers@Sun.COM  * The contents of this file are subject to the terms of the
58005SMark.Powers@Sun.COM  * Common Development and Distribution License (the "License").
68005SMark.Powers@Sun.COM  * You may not use this file except in compliance with the License.
78005SMark.Powers@Sun.COM  *
88005SMark.Powers@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98005SMark.Powers@Sun.COM  * or http://www.opensolaris.org/os/licensing.
108005SMark.Powers@Sun.COM  * See the License for the specific language governing permissions
118005SMark.Powers@Sun.COM  * and limitations under the License.
128005SMark.Powers@Sun.COM  *
138005SMark.Powers@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
148005SMark.Powers@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158005SMark.Powers@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
168005SMark.Powers@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
178005SMark.Powers@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
188005SMark.Powers@Sun.COM  *
198005SMark.Powers@Sun.COM  * CDDL HEADER END
208005SMark.Powers@Sun.COM  */
218005SMark.Powers@Sun.COM /*
22*12826Skuriakose.kuruvilla@oracle.com  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
238005SMark.Powers@Sun.COM  */
248005SMark.Powers@Sun.COM 
2510627Sopensolaris@drydog.com 
268005SMark.Powers@Sun.COM #ifndef _KERNEL
278005SMark.Powers@Sun.COM #include <strings.h>
288005SMark.Powers@Sun.COM #include <limits.h>
298005SMark.Powers@Sun.COM #include <assert.h>
308005SMark.Powers@Sun.COM #include <security/cryptoki.h>
3110627Sopensolaris@drydog.com #endif	/* _KERNEL */
3210627Sopensolaris@drydog.com 
338005SMark.Powers@Sun.COM 
348005SMark.Powers@Sun.COM #include <sys/types.h>
358005SMark.Powers@Sun.COM #include <sys/kmem.h>
368005SMark.Powers@Sun.COM #include <modes/modes.h>
378005SMark.Powers@Sun.COM #include <sys/crypto/common.h>
388005SMark.Powers@Sun.COM #include <sys/crypto/impl.h>
398005SMark.Powers@Sun.COM #include <sys/byteorder.h>
408005SMark.Powers@Sun.COM 
4110627Sopensolaris@drydog.com #ifdef __amd64
4211299Sopensolaris@drydog.com 
4311299Sopensolaris@drydog.com #ifdef _KERNEL
4411299Sopensolaris@drydog.com #include <sys/cpuvar.h>		/* cpu_t, CPU */
45*12826Skuriakose.kuruvilla@oracle.com #include <sys/x86_archext.h>	/* x86_featureset, X86FSET_*, CPUID_* */
4610627Sopensolaris@drydog.com #include <sys/disp.h>		/* kpreempt_disable(), kpreempt_enable */
4710627Sopensolaris@drydog.com /* Workaround for no XMM kernel thread save/restore */
4810627Sopensolaris@drydog.com #define	KPREEMPT_DISABLE	kpreempt_disable()
4910627Sopensolaris@drydog.com #define	KPREEMPT_ENABLE		kpreempt_enable()
5010627Sopensolaris@drydog.com 
5110627Sopensolaris@drydog.com #else
5210627Sopensolaris@drydog.com #include <sys/auxv.h>		/* getisax() */
5310627Sopensolaris@drydog.com #include <sys/auxv_386.h>	/* AV_386_PCLMULQDQ bit */
5410627Sopensolaris@drydog.com #define	KPREEMPT_DISABLE
5510627Sopensolaris@drydog.com #define	KPREEMPT_ENABLE
5610627Sopensolaris@drydog.com #endif	/* _KERNEL */
5710627Sopensolaris@drydog.com 
5810627Sopensolaris@drydog.com extern void gcm_mul_pclmulqdq(uint64_t *x_in, uint64_t *y, uint64_t *res);
5910627Sopensolaris@drydog.com static int intel_pclmulqdq_instruction_present(void);
6010627Sopensolaris@drydog.com #endif	/* __amd64 */
6110627Sopensolaris@drydog.com 
628005SMark.Powers@Sun.COM struct aes_block {
638005SMark.Powers@Sun.COM 	uint64_t a;
648005SMark.Powers@Sun.COM 	uint64_t b;
658005SMark.Powers@Sun.COM };
668005SMark.Powers@Sun.COM 
6710627Sopensolaris@drydog.com 
6810627Sopensolaris@drydog.com /*
6910627Sopensolaris@drydog.com  * gcm_mul()
7010627Sopensolaris@drydog.com  * Perform a carry-less multiplication (that is, use XOR instead of the
7110627Sopensolaris@drydog.com  * multiply operator) on *x_in and *y and place the result in *res.
7210627Sopensolaris@drydog.com  *
7310627Sopensolaris@drydog.com  * Byte swap the input (*x_in and *y) and the output (*res).
7410627Sopensolaris@drydog.com  *
7510627Sopensolaris@drydog.com  * Note: x_in, y, and res all point to 16-byte numbers (an array of two
7610627Sopensolaris@drydog.com  * 64-bit integers).
7710627Sopensolaris@drydog.com  */
788559SMark.Powers@Sun.COM void
gcm_mul(uint64_t * x_in,uint64_t * y,uint64_t * res)798005SMark.Powers@Sun.COM gcm_mul(uint64_t *x_in, uint64_t *y, uint64_t *res)
808005SMark.Powers@Sun.COM {
8110627Sopensolaris@drydog.com #ifdef __amd64
8210627Sopensolaris@drydog.com 	if (intel_pclmulqdq_instruction_present()) {
8310627Sopensolaris@drydog.com 		KPREEMPT_DISABLE;
8410627Sopensolaris@drydog.com 		gcm_mul_pclmulqdq(x_in, y, res);
8510627Sopensolaris@drydog.com 		KPREEMPT_ENABLE;
8610627Sopensolaris@drydog.com 	} else
8710627Sopensolaris@drydog.com #endif	/* __amd64 */
8810627Sopensolaris@drydog.com 	{
8910627Sopensolaris@drydog.com 		static const uint64_t R = 0xe100000000000000ULL;
9010627Sopensolaris@drydog.com 		struct aes_block z = {0, 0};
9110627Sopensolaris@drydog.com 		struct aes_block v;
9210627Sopensolaris@drydog.com 		uint64_t x;
9310627Sopensolaris@drydog.com 		int i, j;
948005SMark.Powers@Sun.COM 
9510627Sopensolaris@drydog.com 		v.a = ntohll(y[0]);
9610627Sopensolaris@drydog.com 		v.b = ntohll(y[1]);
9710627Sopensolaris@drydog.com 
9810627Sopensolaris@drydog.com 		for (j = 0; j < 2; j++) {
9910627Sopensolaris@drydog.com 			x = ntohll(x_in[j]);
10010627Sopensolaris@drydog.com 			for (i = 0; i < 64; i++, x <<= 1) {
10110627Sopensolaris@drydog.com 				if (x & 0x8000000000000000ULL) {
10210627Sopensolaris@drydog.com 					z.a ^= v.a;
10310627Sopensolaris@drydog.com 					z.b ^= v.b;
10410627Sopensolaris@drydog.com 				}
10510627Sopensolaris@drydog.com 				if (v.b & 1ULL) {
10610627Sopensolaris@drydog.com 					v.b = (v.a << 63)|(v.b >> 1);
10710627Sopensolaris@drydog.com 					v.a = (v.a >> 1) ^ R;
10810627Sopensolaris@drydog.com 				} else {
10910627Sopensolaris@drydog.com 					v.b = (v.a << 63)|(v.b >> 1);
11010627Sopensolaris@drydog.com 					v.a = v.a >> 1;
11110627Sopensolaris@drydog.com 				}
1128005SMark.Powers@Sun.COM 			}
1138005SMark.Powers@Sun.COM 		}
11410627Sopensolaris@drydog.com 		res[0] = htonll(z.a);
11510627Sopensolaris@drydog.com 		res[1] = htonll(z.b);
1168005SMark.Powers@Sun.COM 	}
1178005SMark.Powers@Sun.COM }
1188005SMark.Powers@Sun.COM 
11910627Sopensolaris@drydog.com 
1208005SMark.Powers@Sun.COM #define	GHASH(c, d, t) \
1218005SMark.Powers@Sun.COM 	xor_block((uint8_t *)(d), (uint8_t *)(c)->gcm_ghash); \
12211413Sopensolaris@drydog.com 	gcm_mul((uint64_t *)(void *)(c)->gcm_ghash, (c)->gcm_H, \
12311413Sopensolaris@drydog.com 	(uint64_t *)(void *)(t));
12411413Sopensolaris@drydog.com 
1258005SMark.Powers@Sun.COM 
1268005SMark.Powers@Sun.COM /*
1278005SMark.Powers@Sun.COM  * Encrypt multiple blocks of data in GCM mode.  Decrypt for GCM mode
1288005SMark.Powers@Sun.COM  * is done in another function.
1298005SMark.Powers@Sun.COM  */
1308005SMark.Powers@Sun.COM int
gcm_mode_encrypt_contiguous_blocks(gcm_ctx_t * ctx,char * data,size_t length,crypto_data_t * out,size_t block_size,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))1318005SMark.Powers@Sun.COM gcm_mode_encrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length,
1328005SMark.Powers@Sun.COM     crypto_data_t *out, size_t block_size,
1338005SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
1348005SMark.Powers@Sun.COM     void (*copy_block)(uint8_t *, uint8_t *),
1358005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
1368005SMark.Powers@Sun.COM {
1378005SMark.Powers@Sun.COM 	size_t remainder = length;
1388005SMark.Powers@Sun.COM 	size_t need;
1398005SMark.Powers@Sun.COM 	uint8_t *datap = (uint8_t *)data;
1408005SMark.Powers@Sun.COM 	uint8_t *blockp;
1418005SMark.Powers@Sun.COM 	uint8_t *lastp;
1428005SMark.Powers@Sun.COM 	void *iov_or_mp;
1438005SMark.Powers@Sun.COM 	offset_t offset;
1448005SMark.Powers@Sun.COM 	uint8_t *out_data_1;
1458005SMark.Powers@Sun.COM 	uint8_t *out_data_2;
1468005SMark.Powers@Sun.COM 	size_t out_data_1_len;
1478005SMark.Powers@Sun.COM 	uint64_t counter;
1488005SMark.Powers@Sun.COM 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
1498005SMark.Powers@Sun.COM 
1508005SMark.Powers@Sun.COM 	if (length + ctx->gcm_remainder_len < block_size) {
1518005SMark.Powers@Sun.COM 		/* accumulate bytes here and return */
1528005SMark.Powers@Sun.COM 		bcopy(datap,
1538005SMark.Powers@Sun.COM 		    (uint8_t *)ctx->gcm_remainder + ctx->gcm_remainder_len,
1548005SMark.Powers@Sun.COM 		    length);
1558005SMark.Powers@Sun.COM 		ctx->gcm_remainder_len += length;
1568005SMark.Powers@Sun.COM 		ctx->gcm_copy_to = datap;
1578005SMark.Powers@Sun.COM 		return (CRYPTO_SUCCESS);
1588005SMark.Powers@Sun.COM 	}
1598005SMark.Powers@Sun.COM 
1608005SMark.Powers@Sun.COM 	lastp = (uint8_t *)ctx->gcm_cb;
1618005SMark.Powers@Sun.COM 	if (out != NULL)
1628005SMark.Powers@Sun.COM 		crypto_init_ptrs(out, &iov_or_mp, &offset);
1638005SMark.Powers@Sun.COM 
1648005SMark.Powers@Sun.COM 	do {
1658005SMark.Powers@Sun.COM 		/* Unprocessed data from last call. */
1668005SMark.Powers@Sun.COM 		if (ctx->gcm_remainder_len > 0) {
1678005SMark.Powers@Sun.COM 			need = block_size - ctx->gcm_remainder_len;
1688005SMark.Powers@Sun.COM 
1698005SMark.Powers@Sun.COM 			if (need > remainder)
1708005SMark.Powers@Sun.COM 				return (CRYPTO_DATA_LEN_RANGE);
1718005SMark.Powers@Sun.COM 
1728005SMark.Powers@Sun.COM 			bcopy(datap, &((uint8_t *)ctx->gcm_remainder)
1738005SMark.Powers@Sun.COM 			    [ctx->gcm_remainder_len], need);
1748005SMark.Powers@Sun.COM 
1758005SMark.Powers@Sun.COM 			blockp = (uint8_t *)ctx->gcm_remainder;
1768005SMark.Powers@Sun.COM 		} else {
1778005SMark.Powers@Sun.COM 			blockp = datap;
1788005SMark.Powers@Sun.COM 		}
1798005SMark.Powers@Sun.COM 
1808005SMark.Powers@Sun.COM 		/*
1818005SMark.Powers@Sun.COM 		 * Increment counter. Counter bits are confined
1828005SMark.Powers@Sun.COM 		 * to the bottom 32 bits of the counter block.
1838005SMark.Powers@Sun.COM 		 */
1848005SMark.Powers@Sun.COM 		counter = ntohll(ctx->gcm_cb[1] & counter_mask);
1858005SMark.Powers@Sun.COM 		counter = htonll(counter + 1);
1868005SMark.Powers@Sun.COM 		counter &= counter_mask;
1878005SMark.Powers@Sun.COM 		ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
1888005SMark.Powers@Sun.COM 
1898005SMark.Powers@Sun.COM 		encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb,
1908005SMark.Powers@Sun.COM 		    (uint8_t *)ctx->gcm_tmp);
1918005SMark.Powers@Sun.COM 		xor_block(blockp, (uint8_t *)ctx->gcm_tmp);
1928005SMark.Powers@Sun.COM 
1938005SMark.Powers@Sun.COM 		lastp = (uint8_t *)ctx->gcm_tmp;
1948005SMark.Powers@Sun.COM 
1958005SMark.Powers@Sun.COM 		ctx->gcm_processed_data_len += block_size;
1968005SMark.Powers@Sun.COM 
1978005SMark.Powers@Sun.COM 		if (out == NULL) {
1988005SMark.Powers@Sun.COM 			if (ctx->gcm_remainder_len > 0) {
1998005SMark.Powers@Sun.COM 				bcopy(blockp, ctx->gcm_copy_to,
2008005SMark.Powers@Sun.COM 				    ctx->gcm_remainder_len);
2018005SMark.Powers@Sun.COM 				bcopy(blockp + ctx->gcm_remainder_len, datap,
2028005SMark.Powers@Sun.COM 				    need);
2038005SMark.Powers@Sun.COM 			}
2048005SMark.Powers@Sun.COM 		} else {
2058005SMark.Powers@Sun.COM 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
2068005SMark.Powers@Sun.COM 			    &out_data_1_len, &out_data_2, block_size);
2078005SMark.Powers@Sun.COM 
2088005SMark.Powers@Sun.COM 			/* copy block to where it belongs */
2098005SMark.Powers@Sun.COM 			if (out_data_1_len == block_size) {
2108005SMark.Powers@Sun.COM 				copy_block(lastp, out_data_1);
2118005SMark.Powers@Sun.COM 			} else {
2128005SMark.Powers@Sun.COM 				bcopy(lastp, out_data_1, out_data_1_len);
2138005SMark.Powers@Sun.COM 				if (out_data_2 != NULL) {
2148005SMark.Powers@Sun.COM 					bcopy(lastp + out_data_1_len,
2158005SMark.Powers@Sun.COM 					    out_data_2,
2168005SMark.Powers@Sun.COM 					    block_size - out_data_1_len);
2178005SMark.Powers@Sun.COM 				}
2188005SMark.Powers@Sun.COM 			}
2198005SMark.Powers@Sun.COM 			/* update offset */
2208005SMark.Powers@Sun.COM 			out->cd_offset += block_size;
2218005SMark.Powers@Sun.COM 		}
2228005SMark.Powers@Sun.COM 
2238005SMark.Powers@Sun.COM 		/* add ciphertext to the hash */
2248005SMark.Powers@Sun.COM 		GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash);
2258005SMark.Powers@Sun.COM 
2268005SMark.Powers@Sun.COM 		/* Update pointer to next block of data to be processed. */
2278005SMark.Powers@Sun.COM 		if (ctx->gcm_remainder_len != 0) {
2288005SMark.Powers@Sun.COM 			datap += need;
2298005SMark.Powers@Sun.COM 			ctx->gcm_remainder_len = 0;
2308005SMark.Powers@Sun.COM 		} else {
2318005SMark.Powers@Sun.COM 			datap += block_size;
2328005SMark.Powers@Sun.COM 		}
2338005SMark.Powers@Sun.COM 
2348005SMark.Powers@Sun.COM 		remainder = (size_t)&data[length] - (size_t)datap;
2358005SMark.Powers@Sun.COM 
2368005SMark.Powers@Sun.COM 		/* Incomplete last block. */
2378005SMark.Powers@Sun.COM 		if (remainder > 0 && remainder < block_size) {
2388005SMark.Powers@Sun.COM 			bcopy(datap, ctx->gcm_remainder, remainder);
2398005SMark.Powers@Sun.COM 			ctx->gcm_remainder_len = remainder;
2408005SMark.Powers@Sun.COM 			ctx->gcm_copy_to = datap;
2418005SMark.Powers@Sun.COM 			goto out;
2428005SMark.Powers@Sun.COM 		}
2438005SMark.Powers@Sun.COM 		ctx->gcm_copy_to = NULL;
2448005SMark.Powers@Sun.COM 
2458005SMark.Powers@Sun.COM 	} while (remainder > 0);
2468005SMark.Powers@Sun.COM out:
2478005SMark.Powers@Sun.COM 	return (CRYPTO_SUCCESS);
2488005SMark.Powers@Sun.COM }
2498005SMark.Powers@Sun.COM 
2508005SMark.Powers@Sun.COM /* ARGSUSED */
2518005SMark.Powers@Sun.COM int
gcm_encrypt_final(gcm_ctx_t * ctx,crypto_data_t * out,size_t block_size,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))2528005SMark.Powers@Sun.COM gcm_encrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size,
2538005SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
2548005SMark.Powers@Sun.COM     void (*copy_block)(uint8_t *, uint8_t *),
2558005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
2568005SMark.Powers@Sun.COM {
2578005SMark.Powers@Sun.COM 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
2588005SMark.Powers@Sun.COM 	uint8_t *ghash, *macp;
2598005SMark.Powers@Sun.COM 	int i, rv;
2608005SMark.Powers@Sun.COM 
2618005SMark.Powers@Sun.COM 	if (out->cd_length <
2628005SMark.Powers@Sun.COM 	    (ctx->gcm_remainder_len + ctx->gcm_tag_len)) {
2638005SMark.Powers@Sun.COM 		return (CRYPTO_DATA_LEN_RANGE);
2648005SMark.Powers@Sun.COM 	}
2658005SMark.Powers@Sun.COM 
2668005SMark.Powers@Sun.COM 	ghash = (uint8_t *)ctx->gcm_ghash;
2678005SMark.Powers@Sun.COM 
2688005SMark.Powers@Sun.COM 	if (ctx->gcm_remainder_len > 0) {
2698005SMark.Powers@Sun.COM 		uint64_t counter;
2708005SMark.Powers@Sun.COM 		uint8_t *tmpp = (uint8_t *)ctx->gcm_tmp;
2718005SMark.Powers@Sun.COM 
2728005SMark.Powers@Sun.COM 		/*
2738005SMark.Powers@Sun.COM 		 * Here is where we deal with data that is not a
2748005SMark.Powers@Sun.COM 		 * multiple of the block size.
2758005SMark.Powers@Sun.COM 		 */
2768005SMark.Powers@Sun.COM 
2778005SMark.Powers@Sun.COM 		/*
2788005SMark.Powers@Sun.COM 		 * Increment counter.
2798005SMark.Powers@Sun.COM 		 */
2808005SMark.Powers@Sun.COM 		counter = ntohll(ctx->gcm_cb[1] & counter_mask);
2818005SMark.Powers@Sun.COM 		counter = htonll(counter + 1);
2828005SMark.Powers@Sun.COM 		counter &= counter_mask;
2838005SMark.Powers@Sun.COM 		ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
2848005SMark.Powers@Sun.COM 
2858005SMark.Powers@Sun.COM 		encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb,
2868005SMark.Powers@Sun.COM 		    (uint8_t *)ctx->gcm_tmp);
2878005SMark.Powers@Sun.COM 
2888005SMark.Powers@Sun.COM 		macp = (uint8_t *)ctx->gcm_remainder;
2898005SMark.Powers@Sun.COM 		bzero(macp + ctx->gcm_remainder_len,
2908005SMark.Powers@Sun.COM 		    block_size - ctx->gcm_remainder_len);
2918005SMark.Powers@Sun.COM 
2928005SMark.Powers@Sun.COM 		/* XOR with counter block */
2938005SMark.Powers@Sun.COM 		for (i = 0; i < ctx->gcm_remainder_len; i++) {
2948005SMark.Powers@Sun.COM 			macp[i] ^= tmpp[i];
2958005SMark.Powers@Sun.COM 		}
2968005SMark.Powers@Sun.COM 
2978005SMark.Powers@Sun.COM 		/* add ciphertext to the hash */
2988005SMark.Powers@Sun.COM 		GHASH(ctx, macp, ghash);
2998005SMark.Powers@Sun.COM 
3008005SMark.Powers@Sun.COM 		ctx->gcm_processed_data_len += ctx->gcm_remainder_len;
3018005SMark.Powers@Sun.COM 	}
3028005SMark.Powers@Sun.COM 
30311413Sopensolaris@drydog.com 	ctx->gcm_len_a_len_c[1] =
30411413Sopensolaris@drydog.com 	    htonll(CRYPTO_BYTES2BITS(ctx->gcm_processed_data_len));
3058005SMark.Powers@Sun.COM 	GHASH(ctx, ctx->gcm_len_a_len_c, ghash);
3068005SMark.Powers@Sun.COM 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0,
3078005SMark.Powers@Sun.COM 	    (uint8_t *)ctx->gcm_J0);
3088005SMark.Powers@Sun.COM 	xor_block((uint8_t *)ctx->gcm_J0, ghash);
3098005SMark.Powers@Sun.COM 
3108005SMark.Powers@Sun.COM 	if (ctx->gcm_remainder_len > 0) {
3118005SMark.Powers@Sun.COM 		rv = crypto_put_output_data(macp, out, ctx->gcm_remainder_len);
3128005SMark.Powers@Sun.COM 		if (rv != CRYPTO_SUCCESS)
3138005SMark.Powers@Sun.COM 			return (rv);
3148005SMark.Powers@Sun.COM 	}
3158005SMark.Powers@Sun.COM 	out->cd_offset += ctx->gcm_remainder_len;
3168005SMark.Powers@Sun.COM 	ctx->gcm_remainder_len = 0;
3178005SMark.Powers@Sun.COM 	rv = crypto_put_output_data(ghash, out, ctx->gcm_tag_len);
3188005SMark.Powers@Sun.COM 	if (rv != CRYPTO_SUCCESS)
3198005SMark.Powers@Sun.COM 		return (rv);
3208005SMark.Powers@Sun.COM 	out->cd_offset += ctx->gcm_tag_len;
3218005SMark.Powers@Sun.COM 
3228005SMark.Powers@Sun.COM 	return (CRYPTO_SUCCESS);
3238005SMark.Powers@Sun.COM }
3248005SMark.Powers@Sun.COM 
3258005SMark.Powers@Sun.COM /*
3268005SMark.Powers@Sun.COM  * This will only deal with decrypting the last block of the input that
3278005SMark.Powers@Sun.COM  * might not be a multiple of block length.
3288005SMark.Powers@Sun.COM  */
3298005SMark.Powers@Sun.COM static void
gcm_decrypt_incomplete_block(gcm_ctx_t * ctx,size_t block_size,size_t index,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))3308005SMark.Powers@Sun.COM gcm_decrypt_incomplete_block(gcm_ctx_t *ctx, size_t block_size, size_t index,
3318005SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
3328005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
3338005SMark.Powers@Sun.COM {
3348005SMark.Powers@Sun.COM 	uint8_t *datap, *outp, *counterp;
3358005SMark.Powers@Sun.COM 	uint64_t counter;
3368005SMark.Powers@Sun.COM 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
3378005SMark.Powers@Sun.COM 	int i;
3388005SMark.Powers@Sun.COM 
3398005SMark.Powers@Sun.COM 	/*
3408005SMark.Powers@Sun.COM 	 * Increment counter.
3418005SMark.Powers@Sun.COM 	 * Counter bits are confined to the bottom 32 bits
3428005SMark.Powers@Sun.COM 	 */
3438005SMark.Powers@Sun.COM 	counter = ntohll(ctx->gcm_cb[1] & counter_mask);
3448005SMark.Powers@Sun.COM 	counter = htonll(counter + 1);
3458005SMark.Powers@Sun.COM 	counter &= counter_mask;
3468005SMark.Powers@Sun.COM 	ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
3478005SMark.Powers@Sun.COM 
3488005SMark.Powers@Sun.COM 	datap = (uint8_t *)ctx->gcm_remainder;
3498005SMark.Powers@Sun.COM 	outp = &((ctx->gcm_pt_buf)[index]);
3508005SMark.Powers@Sun.COM 	counterp = (uint8_t *)ctx->gcm_tmp;
3518005SMark.Powers@Sun.COM 
3528005SMark.Powers@Sun.COM 	/* authentication tag */
3538005SMark.Powers@Sun.COM 	bzero((uint8_t *)ctx->gcm_tmp, block_size);
3548005SMark.Powers@Sun.COM 	bcopy(datap, (uint8_t *)ctx->gcm_tmp, ctx->gcm_remainder_len);
3558005SMark.Powers@Sun.COM 
3568005SMark.Powers@Sun.COM 	/* add ciphertext to the hash */
3578005SMark.Powers@Sun.COM 	GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash);
3588005SMark.Powers@Sun.COM 
3598005SMark.Powers@Sun.COM 	/* decrypt remaining ciphertext */
3608005SMark.Powers@Sun.COM 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, counterp);
3618005SMark.Powers@Sun.COM 
3628005SMark.Powers@Sun.COM 	/* XOR with counter block */
3638005SMark.Powers@Sun.COM 	for (i = 0; i < ctx->gcm_remainder_len; i++) {
3648005SMark.Powers@Sun.COM 		outp[i] = datap[i] ^ counterp[i];
3658005SMark.Powers@Sun.COM 	}
3668005SMark.Powers@Sun.COM }
3678005SMark.Powers@Sun.COM 
3688005SMark.Powers@Sun.COM /* ARGSUSED */
3698005SMark.Powers@Sun.COM int
gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t * ctx,char * data,size_t length,crypto_data_t * out,size_t block_size,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))3708005SMark.Powers@Sun.COM gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length,
3718005SMark.Powers@Sun.COM     crypto_data_t *out, size_t block_size,
3728005SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
3738005SMark.Powers@Sun.COM     void (*copy_block)(uint8_t *, uint8_t *),
3748005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
3758005SMark.Powers@Sun.COM {
3768005SMark.Powers@Sun.COM 	size_t new_len;
3778005SMark.Powers@Sun.COM 	uint8_t *new;
3788005SMark.Powers@Sun.COM 
3798005SMark.Powers@Sun.COM 	/*
3808005SMark.Powers@Sun.COM 	 * Copy contiguous ciphertext input blocks to plaintext buffer.
3818005SMark.Powers@Sun.COM 	 * Ciphertext will be decrypted in the final.
3828005SMark.Powers@Sun.COM 	 */
3838005SMark.Powers@Sun.COM 	if (length > 0) {
3848005SMark.Powers@Sun.COM 		new_len = ctx->gcm_pt_buf_len + length;
3858005SMark.Powers@Sun.COM #ifdef _KERNEL
3868005SMark.Powers@Sun.COM 		new = kmem_alloc(new_len, ctx->gcm_kmflag);
3878005SMark.Powers@Sun.COM 		bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len);
3888005SMark.Powers@Sun.COM 		kmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len);
3898005SMark.Powers@Sun.COM #else
3908005SMark.Powers@Sun.COM 		new = malloc(new_len);
3918005SMark.Powers@Sun.COM 		bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len);
3928005SMark.Powers@Sun.COM 		free(ctx->gcm_pt_buf);
3938005SMark.Powers@Sun.COM #endif
3948005SMark.Powers@Sun.COM 		if (new == NULL)
3958005SMark.Powers@Sun.COM 			return (CRYPTO_HOST_MEMORY);
3968005SMark.Powers@Sun.COM 
3978005SMark.Powers@Sun.COM 		ctx->gcm_pt_buf = new;
3988005SMark.Powers@Sun.COM 		ctx->gcm_pt_buf_len = new_len;
3998005SMark.Powers@Sun.COM 		bcopy(data, &ctx->gcm_pt_buf[ctx->gcm_processed_data_len],
4008005SMark.Powers@Sun.COM 		    length);
4018005SMark.Powers@Sun.COM 		ctx->gcm_processed_data_len += length;
4028005SMark.Powers@Sun.COM 	}
4038005SMark.Powers@Sun.COM 
4048005SMark.Powers@Sun.COM 	ctx->gcm_remainder_len = 0;
4058005SMark.Powers@Sun.COM 	return (CRYPTO_SUCCESS);
4068005SMark.Powers@Sun.COM }
4078005SMark.Powers@Sun.COM 
4088005SMark.Powers@Sun.COM int
gcm_decrypt_final(gcm_ctx_t * ctx,crypto_data_t * out,size_t block_size,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))4098005SMark.Powers@Sun.COM gcm_decrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size,
4108005SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
4118005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
4128005SMark.Powers@Sun.COM {
4138005SMark.Powers@Sun.COM 	size_t pt_len;
4148005SMark.Powers@Sun.COM 	size_t remainder;
4158005SMark.Powers@Sun.COM 	uint8_t *ghash;
4168005SMark.Powers@Sun.COM 	uint8_t *blockp;
4178005SMark.Powers@Sun.COM 	uint8_t *cbp;
4188005SMark.Powers@Sun.COM 	uint64_t counter;
4198005SMark.Powers@Sun.COM 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
4208005SMark.Powers@Sun.COM 	int processed = 0, rv;
4218005SMark.Powers@Sun.COM 
4228005SMark.Powers@Sun.COM 	ASSERT(ctx->gcm_processed_data_len == ctx->gcm_pt_buf_len);
4238005SMark.Powers@Sun.COM 
4248005SMark.Powers@Sun.COM 	pt_len = ctx->gcm_processed_data_len - ctx->gcm_tag_len;
4258005SMark.Powers@Sun.COM 	ghash = (uint8_t *)ctx->gcm_ghash;
4268005SMark.Powers@Sun.COM 	blockp = ctx->gcm_pt_buf;
4278005SMark.Powers@Sun.COM 	remainder = pt_len;
4288005SMark.Powers@Sun.COM 	while (remainder > 0) {
42911914SMark.Fenwick@Sun.COM 		/* Incomplete last block */
43011914SMark.Fenwick@Sun.COM 		if (remainder < block_size) {
43111914SMark.Fenwick@Sun.COM 			bcopy(blockp, ctx->gcm_remainder, remainder);
43211914SMark.Fenwick@Sun.COM 			ctx->gcm_remainder_len = remainder;
43311914SMark.Fenwick@Sun.COM 			/*
43411914SMark.Fenwick@Sun.COM 			 * not expecting anymore ciphertext, just
43511914SMark.Fenwick@Sun.COM 			 * compute plaintext for the remaining input
43611914SMark.Fenwick@Sun.COM 			 */
43711914SMark.Fenwick@Sun.COM 			gcm_decrypt_incomplete_block(ctx, block_size,
43811914SMark.Fenwick@Sun.COM 			    processed, encrypt_block, xor_block);
43911914SMark.Fenwick@Sun.COM 			ctx->gcm_remainder_len = 0;
44011914SMark.Fenwick@Sun.COM 			goto out;
44111914SMark.Fenwick@Sun.COM 		}
4428005SMark.Powers@Sun.COM 		/* add ciphertext to the hash */
4438005SMark.Powers@Sun.COM 		GHASH(ctx, blockp, ghash);
4448005SMark.Powers@Sun.COM 
4458005SMark.Powers@Sun.COM 		/*
4468005SMark.Powers@Sun.COM 		 * Increment counter.
4478005SMark.Powers@Sun.COM 		 * Counter bits are confined to the bottom 32 bits
4488005SMark.Powers@Sun.COM 		 */
4498005SMark.Powers@Sun.COM 		counter = ntohll(ctx->gcm_cb[1] & counter_mask);
4508005SMark.Powers@Sun.COM 		counter = htonll(counter + 1);
4518005SMark.Powers@Sun.COM 		counter &= counter_mask;
4528005SMark.Powers@Sun.COM 		ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
4538005SMark.Powers@Sun.COM 
4548005SMark.Powers@Sun.COM 		cbp = (uint8_t *)ctx->gcm_tmp;
4558005SMark.Powers@Sun.COM 		encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, cbp);
4568005SMark.Powers@Sun.COM 
4578005SMark.Powers@Sun.COM 		/* XOR with ciphertext */
4588005SMark.Powers@Sun.COM 		xor_block(cbp, blockp);
4598005SMark.Powers@Sun.COM 
4608005SMark.Powers@Sun.COM 		processed += block_size;
4618005SMark.Powers@Sun.COM 		blockp += block_size;
4628005SMark.Powers@Sun.COM 		remainder -= block_size;
4638005SMark.Powers@Sun.COM 	}
4648005SMark.Powers@Sun.COM out:
46511413Sopensolaris@drydog.com 	ctx->gcm_len_a_len_c[1] = htonll(CRYPTO_BYTES2BITS(pt_len));
4668005SMark.Powers@Sun.COM 	GHASH(ctx, ctx->gcm_len_a_len_c, ghash);
4678005SMark.Powers@Sun.COM 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0,
4688005SMark.Powers@Sun.COM 	    (uint8_t *)ctx->gcm_J0);
4698005SMark.Powers@Sun.COM 	xor_block((uint8_t *)ctx->gcm_J0, ghash);
4708005SMark.Powers@Sun.COM 
4718005SMark.Powers@Sun.COM 	/* compare the input authentication tag with what we calculated */
4728005SMark.Powers@Sun.COM 	if (bcmp(&ctx->gcm_pt_buf[pt_len], ghash, ctx->gcm_tag_len)) {
4738005SMark.Powers@Sun.COM 		/* They don't match */
4748005SMark.Powers@Sun.COM 		return (CRYPTO_INVALID_MAC);
4758005SMark.Powers@Sun.COM 	} else {
4768005SMark.Powers@Sun.COM 		rv = crypto_put_output_data(ctx->gcm_pt_buf, out, pt_len);
4778005SMark.Powers@Sun.COM 		if (rv != CRYPTO_SUCCESS)
4788005SMark.Powers@Sun.COM 			return (rv);
4798005SMark.Powers@Sun.COM 		out->cd_offset += pt_len;
4808005SMark.Powers@Sun.COM 	}
4818005SMark.Powers@Sun.COM 	return (CRYPTO_SUCCESS);
4828005SMark.Powers@Sun.COM }
4838005SMark.Powers@Sun.COM 
4848005SMark.Powers@Sun.COM static int
gcm_validate_args(CK_AES_GCM_PARAMS * gcm_param)4858005SMark.Powers@Sun.COM gcm_validate_args(CK_AES_GCM_PARAMS *gcm_param)
4868005SMark.Powers@Sun.COM {
4878005SMark.Powers@Sun.COM 	size_t tag_len;
4888005SMark.Powers@Sun.COM 
4898005SMark.Powers@Sun.COM 	/*
4908005SMark.Powers@Sun.COM 	 * Check the length of the authentication tag (in bits).
4918005SMark.Powers@Sun.COM 	 */
4928005SMark.Powers@Sun.COM 	tag_len = gcm_param->ulTagBits;
4938005SMark.Powers@Sun.COM 	switch (tag_len) {
4948005SMark.Powers@Sun.COM 	case 32:
4958005SMark.Powers@Sun.COM 	case 64:
4968005SMark.Powers@Sun.COM 	case 96:
4978005SMark.Powers@Sun.COM 	case 104:
4988005SMark.Powers@Sun.COM 	case 112:
4998005SMark.Powers@Sun.COM 	case 120:
5008005SMark.Powers@Sun.COM 	case 128:
5018005SMark.Powers@Sun.COM 		break;
5028005SMark.Powers@Sun.COM 	default:
5038005SMark.Powers@Sun.COM 		return (CRYPTO_MECHANISM_PARAM_INVALID);
5048005SMark.Powers@Sun.COM 	}
5058005SMark.Powers@Sun.COM 
5068005SMark.Powers@Sun.COM 	if (gcm_param->ulIvLen == 0)
5078005SMark.Powers@Sun.COM 		return (CRYPTO_MECHANISM_PARAM_INVALID);
5088005SMark.Powers@Sun.COM 
5098005SMark.Powers@Sun.COM 	return (CRYPTO_SUCCESS);
5108005SMark.Powers@Sun.COM }
5118005SMark.Powers@Sun.COM 
5128005SMark.Powers@Sun.COM static void
gcm_format_initial_blocks(uchar_t * iv,ulong_t iv_len,gcm_ctx_t * ctx,size_t block_size,void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))5138005SMark.Powers@Sun.COM gcm_format_initial_blocks(uchar_t *iv, ulong_t iv_len,
5148005SMark.Powers@Sun.COM     gcm_ctx_t *ctx, size_t block_size,
5158005SMark.Powers@Sun.COM     void (*copy_block)(uint8_t *, uint8_t *),
5168005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
5178005SMark.Powers@Sun.COM {
5188005SMark.Powers@Sun.COM 	uint8_t *cb;
5198005SMark.Powers@Sun.COM 	ulong_t remainder = iv_len;
5208005SMark.Powers@Sun.COM 	ulong_t processed = 0;
5218005SMark.Powers@Sun.COM 	uint8_t *datap, *ghash;
5228005SMark.Powers@Sun.COM 	uint64_t len_a_len_c[2];
5238005SMark.Powers@Sun.COM 
5248005SMark.Powers@Sun.COM 	ghash = (uint8_t *)ctx->gcm_ghash;
5258005SMark.Powers@Sun.COM 	cb = (uint8_t *)ctx->gcm_cb;
5268005SMark.Powers@Sun.COM 	if (iv_len == 12) {
5278005SMark.Powers@Sun.COM 		bcopy(iv, cb, 12);
5288005SMark.Powers@Sun.COM 		cb[12] = 0;
5298005SMark.Powers@Sun.COM 		cb[13] = 0;
5308005SMark.Powers@Sun.COM 		cb[14] = 0;
5318005SMark.Powers@Sun.COM 		cb[15] = 1;
5328005SMark.Powers@Sun.COM 		/* J0 will be used again in the final */
5338005SMark.Powers@Sun.COM 		copy_block(cb, (uint8_t *)ctx->gcm_J0);
5348005SMark.Powers@Sun.COM 	} else {
5358005SMark.Powers@Sun.COM 		/* GHASH the IV */
5368005SMark.Powers@Sun.COM 		do {
5378005SMark.Powers@Sun.COM 			if (remainder < block_size) {
5388005SMark.Powers@Sun.COM 				bzero(cb, block_size);
5398005SMark.Powers@Sun.COM 				bcopy(&(iv[processed]), cb, remainder);
5408005SMark.Powers@Sun.COM 				datap = (uint8_t *)cb;
5418005SMark.Powers@Sun.COM 				remainder = 0;
5428005SMark.Powers@Sun.COM 			} else {
5438005SMark.Powers@Sun.COM 				datap = (uint8_t *)(&(iv[processed]));
5448005SMark.Powers@Sun.COM 				processed += block_size;
5458005SMark.Powers@Sun.COM 				remainder -= block_size;
5468005SMark.Powers@Sun.COM 			}
5478005SMark.Powers@Sun.COM 			GHASH(ctx, datap, ghash);
5488005SMark.Powers@Sun.COM 		} while (remainder > 0);
5498005SMark.Powers@Sun.COM 
5508005SMark.Powers@Sun.COM 		len_a_len_c[0] = 0;
55111413Sopensolaris@drydog.com 		len_a_len_c[1] = htonll(CRYPTO_BYTES2BITS(iv_len));
5528005SMark.Powers@Sun.COM 		GHASH(ctx, len_a_len_c, ctx->gcm_J0);
5538005SMark.Powers@Sun.COM 
5548005SMark.Powers@Sun.COM 		/* J0 will be used again in the final */
5558005SMark.Powers@Sun.COM 		copy_block((uint8_t *)ctx->gcm_J0, (uint8_t *)cb);
5568005SMark.Powers@Sun.COM 	}
5578005SMark.Powers@Sun.COM }
5588005SMark.Powers@Sun.COM 
5598005SMark.Powers@Sun.COM /*
5608005SMark.Powers@Sun.COM  * The following function is called at encrypt or decrypt init time
5618005SMark.Powers@Sun.COM  * for AES GCM mode.
5628005SMark.Powers@Sun.COM  */
5638005SMark.Powers@Sun.COM int
gcm_init(gcm_ctx_t * ctx,unsigned char * iv,size_t iv_len,unsigned char * auth_data,size_t auth_data_len,size_t block_size,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))5648005SMark.Powers@Sun.COM gcm_init(gcm_ctx_t *ctx, unsigned char *iv, size_t iv_len,
5658005SMark.Powers@Sun.COM     unsigned char *auth_data, size_t auth_data_len, size_t block_size,
5668005SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
5678005SMark.Powers@Sun.COM     void (*copy_block)(uint8_t *, uint8_t *),
5688005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
5698005SMark.Powers@Sun.COM {
5708005SMark.Powers@Sun.COM 	uint8_t *ghash, *datap, *authp;
5718005SMark.Powers@Sun.COM 	size_t remainder, processed;
5728005SMark.Powers@Sun.COM 
5738005SMark.Powers@Sun.COM 	/* encrypt zero block to get subkey H */
5748005SMark.Powers@Sun.COM 	bzero(ctx->gcm_H, sizeof (ctx->gcm_H));
5758005SMark.Powers@Sun.COM 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_H,
5768005SMark.Powers@Sun.COM 	    (uint8_t *)ctx->gcm_H);
5778005SMark.Powers@Sun.COM 
5788005SMark.Powers@Sun.COM 	gcm_format_initial_blocks(iv, iv_len, ctx, block_size,
5798005SMark.Powers@Sun.COM 	    copy_block, xor_block);
5808005SMark.Powers@Sun.COM 
5818005SMark.Powers@Sun.COM 	authp = (uint8_t *)ctx->gcm_tmp;
5828005SMark.Powers@Sun.COM 	ghash = (uint8_t *)ctx->gcm_ghash;
5838005SMark.Powers@Sun.COM 	bzero(authp, block_size);
5848005SMark.Powers@Sun.COM 	bzero(ghash, block_size);
5858005SMark.Powers@Sun.COM 
5868005SMark.Powers@Sun.COM 	processed = 0;
5878005SMark.Powers@Sun.COM 	remainder = auth_data_len;
5888005SMark.Powers@Sun.COM 	do {
5898005SMark.Powers@Sun.COM 		if (remainder < block_size) {
5908005SMark.Powers@Sun.COM 			/*
5918005SMark.Powers@Sun.COM 			 * There's not a block full of data, pad rest of
5928005SMark.Powers@Sun.COM 			 * buffer with zero
5938005SMark.Powers@Sun.COM 			 */
5948005SMark.Powers@Sun.COM 			bzero(authp, block_size);
5958005SMark.Powers@Sun.COM 			bcopy(&(auth_data[processed]), authp, remainder);
5968005SMark.Powers@Sun.COM 			datap = (uint8_t *)authp;
5978005SMark.Powers@Sun.COM 			remainder = 0;
5988005SMark.Powers@Sun.COM 		} else {
5998005SMark.Powers@Sun.COM 			datap = (uint8_t *)(&(auth_data[processed]));
6008005SMark.Powers@Sun.COM 			processed += block_size;
6018005SMark.Powers@Sun.COM 			remainder -= block_size;
6028005SMark.Powers@Sun.COM 		}
6038005SMark.Powers@Sun.COM 
6048005SMark.Powers@Sun.COM 		/* add auth data to the hash */
6058005SMark.Powers@Sun.COM 		GHASH(ctx, datap, ghash);
6068005SMark.Powers@Sun.COM 
6078005SMark.Powers@Sun.COM 	} while (remainder > 0);
6088005SMark.Powers@Sun.COM 
6098005SMark.Powers@Sun.COM 	return (CRYPTO_SUCCESS);
6108005SMark.Powers@Sun.COM }
6118005SMark.Powers@Sun.COM 
6128005SMark.Powers@Sun.COM int
gcm_init_ctx(gcm_ctx_t * gcm_ctx,char * param,size_t block_size,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))6138005SMark.Powers@Sun.COM gcm_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size,
6148005SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
6158005SMark.Powers@Sun.COM     void (*copy_block)(uint8_t *, uint8_t *),
6168005SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
6178005SMark.Powers@Sun.COM {
6188005SMark.Powers@Sun.COM 	int rv;
6198005SMark.Powers@Sun.COM 	CK_AES_GCM_PARAMS *gcm_param;
6208005SMark.Powers@Sun.COM 
6218005SMark.Powers@Sun.COM 	if (param != NULL) {
62211413Sopensolaris@drydog.com 		gcm_param = (CK_AES_GCM_PARAMS *)(void *)param;
6238005SMark.Powers@Sun.COM 
6248005SMark.Powers@Sun.COM 		if ((rv = gcm_validate_args(gcm_param)) != 0) {
6258005SMark.Powers@Sun.COM 			return (rv);
6268005SMark.Powers@Sun.COM 		}
6278005SMark.Powers@Sun.COM 
6288005SMark.Powers@Sun.COM 		gcm_ctx->gcm_tag_len = gcm_param->ulTagBits;
6298005SMark.Powers@Sun.COM 		gcm_ctx->gcm_tag_len >>= 3;
6308005SMark.Powers@Sun.COM 		gcm_ctx->gcm_processed_data_len = 0;
6318005SMark.Powers@Sun.COM 
6328005SMark.Powers@Sun.COM 		/* these values are in bits */
63311413Sopensolaris@drydog.com 		gcm_ctx->gcm_len_a_len_c[0]
63411413Sopensolaris@drydog.com 		    = htonll(CRYPTO_BYTES2BITS(gcm_param->ulAADLen));
6358005SMark.Powers@Sun.COM 
6368005SMark.Powers@Sun.COM 		rv = CRYPTO_SUCCESS;
6378005SMark.Powers@Sun.COM 		gcm_ctx->gcm_flags |= GCM_MODE;
6388005SMark.Powers@Sun.COM 	} else {
6398005SMark.Powers@Sun.COM 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
6408005SMark.Powers@Sun.COM 		goto out;
6418005SMark.Powers@Sun.COM 	}
6428005SMark.Powers@Sun.COM 
6438005SMark.Powers@Sun.COM 	if (gcm_init(gcm_ctx, gcm_param->pIv, gcm_param->ulIvLen,
6448005SMark.Powers@Sun.COM 	    gcm_param->pAAD, gcm_param->ulAADLen, block_size,
6458005SMark.Powers@Sun.COM 	    encrypt_block, copy_block, xor_block) != 0) {
6468005SMark.Powers@Sun.COM 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
6478005SMark.Powers@Sun.COM 	}
6488005SMark.Powers@Sun.COM out:
6498005SMark.Powers@Sun.COM 	return (rv);
6508005SMark.Powers@Sun.COM }
6518005SMark.Powers@Sun.COM 
6529339SMark.Powers@Sun.COM int
gmac_init_ctx(gcm_ctx_t * gcm_ctx,char * param,size_t block_size,int (* encrypt_block)(const void *,const uint8_t *,uint8_t *),void (* copy_block)(uint8_t *,uint8_t *),void (* xor_block)(uint8_t *,uint8_t *))6539339SMark.Powers@Sun.COM gmac_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size,
6549339SMark.Powers@Sun.COM     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
6559339SMark.Powers@Sun.COM     void (*copy_block)(uint8_t *, uint8_t *),
6569339SMark.Powers@Sun.COM     void (*xor_block)(uint8_t *, uint8_t *))
6579339SMark.Powers@Sun.COM {
6589339SMark.Powers@Sun.COM 	int rv;
6599339SMark.Powers@Sun.COM 	CK_AES_GMAC_PARAMS *gmac_param;
6609339SMark.Powers@Sun.COM 
6619339SMark.Powers@Sun.COM 	if (param != NULL) {
66211413Sopensolaris@drydog.com 		gmac_param = (CK_AES_GMAC_PARAMS *)(void *)param;
6639339SMark.Powers@Sun.COM 
6649339SMark.Powers@Sun.COM 		gcm_ctx->gcm_tag_len = CRYPTO_BITS2BYTES(AES_GMAC_TAG_BITS);
6659339SMark.Powers@Sun.COM 		gcm_ctx->gcm_processed_data_len = 0;
6669339SMark.Powers@Sun.COM 
6679339SMark.Powers@Sun.COM 		/* these values are in bits */
66811413Sopensolaris@drydog.com 		gcm_ctx->gcm_len_a_len_c[0]
66911413Sopensolaris@drydog.com 		    = htonll(CRYPTO_BYTES2BITS(gmac_param->ulAADLen));
6709339SMark.Powers@Sun.COM 
6719339SMark.Powers@Sun.COM 		rv = CRYPTO_SUCCESS;
6729339SMark.Powers@Sun.COM 		gcm_ctx->gcm_flags |= GMAC_MODE;
6739339SMark.Powers@Sun.COM 	} else {
6749339SMark.Powers@Sun.COM 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
6759339SMark.Powers@Sun.COM 		goto out;
6769339SMark.Powers@Sun.COM 	}
6779339SMark.Powers@Sun.COM 
6789339SMark.Powers@Sun.COM 	if (gcm_init(gcm_ctx, gmac_param->pIv, AES_GMAC_IV_LEN,
6799339SMark.Powers@Sun.COM 	    gmac_param->pAAD, gmac_param->ulAADLen, block_size,
6809339SMark.Powers@Sun.COM 	    encrypt_block, copy_block, xor_block) != 0) {
6819339SMark.Powers@Sun.COM 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
6829339SMark.Powers@Sun.COM 	}
6839339SMark.Powers@Sun.COM out:
6849339SMark.Powers@Sun.COM 	return (rv);
6859339SMark.Powers@Sun.COM }
6869339SMark.Powers@Sun.COM 
6878005SMark.Powers@Sun.COM void *
gcm_alloc_ctx(int kmflag)6888005SMark.Powers@Sun.COM gcm_alloc_ctx(int kmflag)
6898005SMark.Powers@Sun.COM {
6908005SMark.Powers@Sun.COM 	gcm_ctx_t *gcm_ctx;
6918005SMark.Powers@Sun.COM 
6928005SMark.Powers@Sun.COM #ifdef _KERNEL
6938005SMark.Powers@Sun.COM 	if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL)
6948005SMark.Powers@Sun.COM #else
6958005SMark.Powers@Sun.COM 	if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL)
6968005SMark.Powers@Sun.COM #endif
6978005SMark.Powers@Sun.COM 		return (NULL);
6988005SMark.Powers@Sun.COM 
6998005SMark.Powers@Sun.COM 	gcm_ctx->gcm_flags = GCM_MODE;
7008005SMark.Powers@Sun.COM 	return (gcm_ctx);
7018005SMark.Powers@Sun.COM }
7028005SMark.Powers@Sun.COM 
7039339SMark.Powers@Sun.COM void *
gmac_alloc_ctx(int kmflag)7049339SMark.Powers@Sun.COM gmac_alloc_ctx(int kmflag)
7059339SMark.Powers@Sun.COM {
7069339SMark.Powers@Sun.COM 	gcm_ctx_t *gcm_ctx;
7079339SMark.Powers@Sun.COM 
7089339SMark.Powers@Sun.COM #ifdef _KERNEL
7099339SMark.Powers@Sun.COM 	if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL)
7109339SMark.Powers@Sun.COM #else
7119339SMark.Powers@Sun.COM 	if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL)
7129339SMark.Powers@Sun.COM #endif
7139339SMark.Powers@Sun.COM 		return (NULL);
7149339SMark.Powers@Sun.COM 
7159339SMark.Powers@Sun.COM 	gcm_ctx->gcm_flags = GMAC_MODE;
7169339SMark.Powers@Sun.COM 	return (gcm_ctx);
7179339SMark.Powers@Sun.COM }
7189339SMark.Powers@Sun.COM 
7198005SMark.Powers@Sun.COM void
gcm_set_kmflag(gcm_ctx_t * ctx,int kmflag)7208005SMark.Powers@Sun.COM gcm_set_kmflag(gcm_ctx_t *ctx, int kmflag)
7218005SMark.Powers@Sun.COM {
7228005SMark.Powers@Sun.COM 	ctx->gcm_kmflag = kmflag;
7238005SMark.Powers@Sun.COM }
72410627Sopensolaris@drydog.com 
72510627Sopensolaris@drydog.com 
72610627Sopensolaris@drydog.com #ifdef __amd64
72710627Sopensolaris@drydog.com /*
72810627Sopensolaris@drydog.com  * Return 1 if executing on Intel with PCLMULQDQ instructions,
72910627Sopensolaris@drydog.com  * otherwise 0 (i.e., Intel without PCLMULQDQ or AMD64).
73010627Sopensolaris@drydog.com  * Cache the result, as the CPU can't change.
73110627Sopensolaris@drydog.com  *
73210627Sopensolaris@drydog.com  * Note: the userland version uses getisax().  The kernel version uses
733*12826Skuriakose.kuruvilla@oracle.com  * is_x86_featureset().
73410627Sopensolaris@drydog.com  */
73510627Sopensolaris@drydog.com static int
intel_pclmulqdq_instruction_present(void)73610627Sopensolaris@drydog.com intel_pclmulqdq_instruction_present(void)
73710627Sopensolaris@drydog.com {
73810627Sopensolaris@drydog.com 	static int	cached_result = -1;
73910627Sopensolaris@drydog.com 
74010627Sopensolaris@drydog.com 	if (cached_result == -1) { /* first time */
74110627Sopensolaris@drydog.com #ifdef _KERNEL
742*12826Skuriakose.kuruvilla@oracle.com 		cached_result =
743*12826Skuriakose.kuruvilla@oracle.com 		    is_x86_feature(x86_featureset, X86FSET_PCLMULQDQ);
74410627Sopensolaris@drydog.com #else
74510627Sopensolaris@drydog.com 		uint_t		ui = 0;
74610627Sopensolaris@drydog.com 
74710627Sopensolaris@drydog.com 		(void) getisax(&ui, 1);
74810627Sopensolaris@drydog.com 		cached_result = (ui & AV_386_PCLMULQDQ) != 0;
74910627Sopensolaris@drydog.com #endif	/* _KERNEL */
75010627Sopensolaris@drydog.com 	}
75110627Sopensolaris@drydog.com 
75210627Sopensolaris@drydog.com 	return (cached_result);
75310627Sopensolaris@drydog.com }
75410627Sopensolaris@drydog.com #endif	/* __amd64 */
755