xref: /illumos-gate/usr/src/common/crypto/modes/gcm.c (revision fb261280)
14d703b5cSMark Powers /*
24d703b5cSMark Powers  * CDDL HEADER START
34d703b5cSMark Powers  *
44d703b5cSMark Powers  * The contents of this file are subject to the terms of the
54d703b5cSMark Powers  * Common Development and Distribution License (the "License").
64d703b5cSMark Powers  * You may not use this file except in compliance with the License.
74d703b5cSMark Powers  *
84d703b5cSMark Powers  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94d703b5cSMark Powers  * or http://www.opensolaris.org/os/licensing.
104d703b5cSMark Powers  * See the License for the specific language governing permissions
114d703b5cSMark Powers  * and limitations under the License.
124d703b5cSMark Powers  *
134d703b5cSMark Powers  * When distributing Covered Code, include this CDDL HEADER in each
144d703b5cSMark Powers  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154d703b5cSMark Powers  * If applicable, add the following below this CDDL HEADER, with the
164d703b5cSMark Powers  * fields enclosed by brackets "[]" replaced with your own identifying
174d703b5cSMark Powers  * information: Portions Copyright [yyyy] [name of copyright owner]
184d703b5cSMark Powers  *
194d703b5cSMark Powers  * CDDL HEADER END
204d703b5cSMark Powers  */
214d703b5cSMark Powers /*
227417cfdeSKuriakose Kuruvilla  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23*fb261280SJason King  * Copyright 2018, Joyent, Inc.
244d703b5cSMark Powers  */
254d703b5cSMark Powers 
26104d3bdeSDan OpenSolaris Anderson 
274d703b5cSMark Powers #ifndef _KERNEL
284d703b5cSMark Powers #include <strings.h>
294d703b5cSMark Powers #include <limits.h>
304d703b5cSMark Powers #include <security/cryptoki.h>
31104d3bdeSDan OpenSolaris Anderson #endif	/* _KERNEL */
32104d3bdeSDan OpenSolaris Anderson 
33*fb261280SJason King #include <sys/debug.h>
344d703b5cSMark Powers #include <sys/types.h>
354d703b5cSMark Powers #include <sys/kmem.h>
364d703b5cSMark Powers #include <modes/modes.h>
374d703b5cSMark Powers #include <sys/crypto/common.h>
384d703b5cSMark Powers #include <sys/crypto/impl.h>
394d703b5cSMark Powers #include <sys/byteorder.h>
404d703b5cSMark Powers 
41104d3bdeSDan OpenSolaris Anderson #ifdef __amd64
42104d3bdeSDan OpenSolaris Anderson 
4395fddab5SDan OpenSolaris Anderson #ifdef _KERNEL
44104d3bdeSDan OpenSolaris Anderson #include <sys/cpuvar.h>		/* cpu_t, CPU */
457417cfdeSKuriakose Kuruvilla #include <sys/x86_archext.h>	/* x86_featureset, X86FSET_*, CPUID_* */
46104d3bdeSDan OpenSolaris Anderson #include <sys/disp.h>		/* kpreempt_disable(), kpreempt_enable */
47104d3bdeSDan OpenSolaris Anderson /* Workaround for no XMM kernel thread save/restore */
48104d3bdeSDan OpenSolaris Anderson #define	KPREEMPT_DISABLE	kpreempt_disable()
49104d3bdeSDan OpenSolaris Anderson #define	KPREEMPT_ENABLE		kpreempt_enable()
50104d3bdeSDan OpenSolaris Anderson 
51104d3bdeSDan OpenSolaris Anderson #else
52104d3bdeSDan OpenSolaris Anderson #include <sys/auxv.h>		/* getisax() */
53104d3bdeSDan OpenSolaris Anderson #include <sys/auxv_386.h>	/* AV_386_PCLMULQDQ bit */
54104d3bdeSDan OpenSolaris Anderson #define	KPREEMPT_DISABLE
55104d3bdeSDan OpenSolaris Anderson #define	KPREEMPT_ENABLE
56104d3bdeSDan OpenSolaris Anderson #endif	/* _KERNEL */
57104d3bdeSDan OpenSolaris Anderson 
58104d3bdeSDan OpenSolaris Anderson extern void gcm_mul_pclmulqdq(uint64_t *x_in, uint64_t *y, uint64_t *res);
59104d3bdeSDan OpenSolaris Anderson static int intel_pclmulqdq_instruction_present(void);
60104d3bdeSDan OpenSolaris Anderson #endif	/* __amd64 */
61104d3bdeSDan OpenSolaris Anderson 
624d703b5cSMark Powers struct aes_block {
634d703b5cSMark Powers 	uint64_t a;
644d703b5cSMark Powers 	uint64_t b;
654d703b5cSMark Powers };
664d703b5cSMark Powers 
67104d3bdeSDan OpenSolaris Anderson 
68104d3bdeSDan OpenSolaris Anderson /*
69104d3bdeSDan OpenSolaris Anderson  * gcm_mul()
70104d3bdeSDan OpenSolaris Anderson  * Perform a carry-less multiplication (that is, use XOR instead of the
71104d3bdeSDan OpenSolaris Anderson  * multiply operator) on *x_in and *y and place the result in *res.
72104d3bdeSDan OpenSolaris Anderson  *
73104d3bdeSDan OpenSolaris Anderson  * Byte swap the input (*x_in and *y) and the output (*res).
74104d3bdeSDan OpenSolaris Anderson  *
75104d3bdeSDan OpenSolaris Anderson  * Note: x_in, y, and res all point to 16-byte numbers (an array of two
76104d3bdeSDan OpenSolaris Anderson  * 64-bit integers).
77104d3bdeSDan OpenSolaris Anderson  */
78e8c016efSMark Powers void
gcm_mul(uint64_t * x_in,uint64_t * y,uint64_t * res)794d703b5cSMark Powers gcm_mul(uint64_t *x_in, uint64_t *y, uint64_t *res)
804d703b5cSMark Powers {
81104d3bdeSDan OpenSolaris Anderson #ifdef __amd64
82104d3bdeSDan OpenSolaris Anderson 	if (intel_pclmulqdq_instruction_present()) {
83104d3bdeSDan OpenSolaris Anderson 		KPREEMPT_DISABLE;
84104d3bdeSDan OpenSolaris Anderson 		gcm_mul_pclmulqdq(x_in, y, res);
85104d3bdeSDan OpenSolaris Anderson 		KPREEMPT_ENABLE;
86104d3bdeSDan OpenSolaris Anderson 	} else
87104d3bdeSDan OpenSolaris Anderson #endif	/* __amd64 */
88104d3bdeSDan OpenSolaris Anderson 	{
89104d3bdeSDan OpenSolaris Anderson 		static const uint64_t R = 0xe100000000000000ULL;
904d703b5cSMark Powers 		struct aes_block z = {0, 0};
914d703b5cSMark Powers 		struct aes_block v;
924d703b5cSMark Powers 		uint64_t x;
934d703b5cSMark Powers 		int i, j;
944d703b5cSMark Powers 
954d703b5cSMark Powers 		v.a = ntohll(y[0]);
964d703b5cSMark Powers 		v.b = ntohll(y[1]);
974d703b5cSMark Powers 
984d703b5cSMark Powers 		for (j = 0; j < 2; j++) {
994d703b5cSMark Powers 			x = ntohll(x_in[j]);
1004d703b5cSMark Powers 			for (i = 0; i < 64; i++, x <<= 1) {
1014d703b5cSMark Powers 				if (x & 0x8000000000000000ULL) {
1024d703b5cSMark Powers 					z.a ^= v.a;
1034d703b5cSMark Powers 					z.b ^= v.b;
1044d703b5cSMark Powers 				}
1054d703b5cSMark Powers 				if (v.b & 1ULL) {
1064d703b5cSMark Powers 					v.b = (v.a << 63)|(v.b >> 1);
1074d703b5cSMark Powers 					v.a = (v.a >> 1) ^ R;
1084d703b5cSMark Powers 				} else {
1094d703b5cSMark Powers 					v.b = (v.a << 63)|(v.b >> 1);
1104d703b5cSMark Powers 					v.a = v.a >> 1;
1114d703b5cSMark Powers 				}
1124d703b5cSMark Powers 			}
1134d703b5cSMark Powers 		}
1144d703b5cSMark Powers 		res[0] = htonll(z.a);
1154d703b5cSMark Powers 		res[1] = htonll(z.b);
1164d703b5cSMark Powers 	}
117104d3bdeSDan OpenSolaris Anderson }
118104d3bdeSDan OpenSolaris Anderson 
1194d703b5cSMark Powers 
1204d703b5cSMark Powers #define	GHASH(c, d, t) \
1214d703b5cSMark Powers 	xor_block((uint8_t *)(d), (uint8_t *)(c)->gcm_ghash); \
12295014fbbSDan OpenSolaris Anderson 	gcm_mul((uint64_t *)(void *)(c)->gcm_ghash, (c)->gcm_H, \
12395014fbbSDan OpenSolaris Anderson 	(uint64_t *)(void *)(t));
12495014fbbSDan OpenSolaris Anderson 
1254d703b5cSMark Powers 
1264d703b5cSMark Powers /*
1274d703b5cSMark Powers  * Encrypt multiple blocks of data in GCM mode.  Decrypt for GCM mode
1284d703b5cSMark Powers  * is done in another function.
1294d703b5cSMark Powers  */
1304d703b5cSMark Powers 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 *))1314d703b5cSMark Powers gcm_mode_encrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length,
1324d703b5cSMark Powers     crypto_data_t *out, size_t block_size,
1334d703b5cSMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
1344d703b5cSMark Powers     void (*copy_block)(uint8_t *, uint8_t *),
1354d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
1364d703b5cSMark Powers {
1374d703b5cSMark Powers 	size_t remainder = length;
1384d703b5cSMark Powers 	size_t need;
1394d703b5cSMark Powers 	uint8_t *datap = (uint8_t *)data;
1404d703b5cSMark Powers 	uint8_t *blockp;
1414d703b5cSMark Powers 	uint8_t *lastp;
1424d703b5cSMark Powers 	void *iov_or_mp;
1434d703b5cSMark Powers 	offset_t offset;
1444d703b5cSMark Powers 	uint8_t *out_data_1;
1454d703b5cSMark Powers 	uint8_t *out_data_2;
1464d703b5cSMark Powers 	size_t out_data_1_len;
1474d703b5cSMark Powers 	uint64_t counter;
1484d703b5cSMark Powers 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
1494d703b5cSMark Powers 
1504d703b5cSMark Powers 	if (length + ctx->gcm_remainder_len < block_size) {
1514d703b5cSMark Powers 		/* accumulate bytes here and return */
1524d703b5cSMark Powers 		bcopy(datap,
1534d703b5cSMark Powers 		    (uint8_t *)ctx->gcm_remainder + ctx->gcm_remainder_len,
1544d703b5cSMark Powers 		    length);
1554d703b5cSMark Powers 		ctx->gcm_remainder_len += length;
1564d703b5cSMark Powers 		ctx->gcm_copy_to = datap;
1574d703b5cSMark Powers 		return (CRYPTO_SUCCESS);
1584d703b5cSMark Powers 	}
1594d703b5cSMark Powers 
1604d703b5cSMark Powers 	lastp = (uint8_t *)ctx->gcm_cb;
1614d703b5cSMark Powers 	if (out != NULL)
1624d703b5cSMark Powers 		crypto_init_ptrs(out, &iov_or_mp, &offset);
1634d703b5cSMark Powers 
1644d703b5cSMark Powers 	do {
1654d703b5cSMark Powers 		/* Unprocessed data from last call. */
1664d703b5cSMark Powers 		if (ctx->gcm_remainder_len > 0) {
1674d703b5cSMark Powers 			need = block_size - ctx->gcm_remainder_len;
1684d703b5cSMark Powers 
1694d703b5cSMark Powers 			if (need > remainder)
1704d703b5cSMark Powers 				return (CRYPTO_DATA_LEN_RANGE);
1714d703b5cSMark Powers 
1724d703b5cSMark Powers 			bcopy(datap, &((uint8_t *)ctx->gcm_remainder)
1734d703b5cSMark Powers 			    [ctx->gcm_remainder_len], need);
1744d703b5cSMark Powers 
1754d703b5cSMark Powers 			blockp = (uint8_t *)ctx->gcm_remainder;
1764d703b5cSMark Powers 		} else {
1774d703b5cSMark Powers 			blockp = datap;
1784d703b5cSMark Powers 		}
1794d703b5cSMark Powers 
1804d703b5cSMark Powers 		/*
1814d703b5cSMark Powers 		 * Increment counter. Counter bits are confined
1824d703b5cSMark Powers 		 * to the bottom 32 bits of the counter block.
1834d703b5cSMark Powers 		 */
1844d703b5cSMark Powers 		counter = ntohll(ctx->gcm_cb[1] & counter_mask);
1854d703b5cSMark Powers 		counter = htonll(counter + 1);
1864d703b5cSMark Powers 		counter &= counter_mask;
1874d703b5cSMark Powers 		ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
1884d703b5cSMark Powers 
1894d703b5cSMark Powers 		encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb,
1904d703b5cSMark Powers 		    (uint8_t *)ctx->gcm_tmp);
1914d703b5cSMark Powers 		xor_block(blockp, (uint8_t *)ctx->gcm_tmp);
1924d703b5cSMark Powers 
1934d703b5cSMark Powers 		lastp = (uint8_t *)ctx->gcm_tmp;
1944d703b5cSMark Powers 
1954d703b5cSMark Powers 		ctx->gcm_processed_data_len += block_size;
1964d703b5cSMark Powers 
1974d703b5cSMark Powers 		if (out == NULL) {
1984d703b5cSMark Powers 			if (ctx->gcm_remainder_len > 0) {
1994d703b5cSMark Powers 				bcopy(blockp, ctx->gcm_copy_to,
2004d703b5cSMark Powers 				    ctx->gcm_remainder_len);
2014d703b5cSMark Powers 				bcopy(blockp + ctx->gcm_remainder_len, datap,
2024d703b5cSMark Powers 				    need);
2034d703b5cSMark Powers 			}
2044d703b5cSMark Powers 		} else {
2054d703b5cSMark Powers 			crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1,
2064d703b5cSMark Powers 			    &out_data_1_len, &out_data_2, block_size);
2074d703b5cSMark Powers 
2084d703b5cSMark Powers 			/* copy block to where it belongs */
2094d703b5cSMark Powers 			if (out_data_1_len == block_size) {
2104d703b5cSMark Powers 				copy_block(lastp, out_data_1);
2114d703b5cSMark Powers 			} else {
2124d703b5cSMark Powers 				bcopy(lastp, out_data_1, out_data_1_len);
2134d703b5cSMark Powers 				if (out_data_2 != NULL) {
2144d703b5cSMark Powers 					bcopy(lastp + out_data_1_len,
2154d703b5cSMark Powers 					    out_data_2,
2164d703b5cSMark Powers 					    block_size - out_data_1_len);
2174d703b5cSMark Powers 				}
2184d703b5cSMark Powers 			}
2194d703b5cSMark Powers 			/* update offset */
2204d703b5cSMark Powers 			out->cd_offset += block_size;
2214d703b5cSMark Powers 		}
2224d703b5cSMark Powers 
2234d703b5cSMark Powers 		/* add ciphertext to the hash */
2244d703b5cSMark Powers 		GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash);
2254d703b5cSMark Powers 
2264d703b5cSMark Powers 		/* Update pointer to next block of data to be processed. */
2274d703b5cSMark Powers 		if (ctx->gcm_remainder_len != 0) {
2284d703b5cSMark Powers 			datap += need;
2294d703b5cSMark Powers 			ctx->gcm_remainder_len = 0;
2304d703b5cSMark Powers 		} else {
2314d703b5cSMark Powers 			datap += block_size;
2324d703b5cSMark Powers 		}
2334d703b5cSMark Powers 
2344d703b5cSMark Powers 		remainder = (size_t)&data[length] - (size_t)datap;
2354d703b5cSMark Powers 
2364d703b5cSMark Powers 		/* Incomplete last block. */
2374d703b5cSMark Powers 		if (remainder > 0 && remainder < block_size) {
2384d703b5cSMark Powers 			bcopy(datap, ctx->gcm_remainder, remainder);
2394d703b5cSMark Powers 			ctx->gcm_remainder_len = remainder;
2404d703b5cSMark Powers 			ctx->gcm_copy_to = datap;
2414d703b5cSMark Powers 			goto out;
2424d703b5cSMark Powers 		}
2434d703b5cSMark Powers 		ctx->gcm_copy_to = NULL;
2444d703b5cSMark Powers 
2454d703b5cSMark Powers 	} while (remainder > 0);
2464d703b5cSMark Powers out:
2474d703b5cSMark Powers 	return (CRYPTO_SUCCESS);
2484d703b5cSMark Powers }
2494d703b5cSMark Powers 
2504d703b5cSMark Powers /* ARGSUSED */
2514d703b5cSMark Powers 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 *))2524d703b5cSMark Powers gcm_encrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size,
2534d703b5cSMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
2544d703b5cSMark Powers     void (*copy_block)(uint8_t *, uint8_t *),
2554d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
2564d703b5cSMark Powers {
2574d703b5cSMark Powers 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
2584d703b5cSMark Powers 	uint8_t *ghash, *macp;
2594d703b5cSMark Powers 	int i, rv;
2604d703b5cSMark Powers 
2614d703b5cSMark Powers 	if (out->cd_length <
2624d703b5cSMark Powers 	    (ctx->gcm_remainder_len + ctx->gcm_tag_len)) {
2634d703b5cSMark Powers 		return (CRYPTO_DATA_LEN_RANGE);
2644d703b5cSMark Powers 	}
2654d703b5cSMark Powers 
2664d703b5cSMark Powers 	ghash = (uint8_t *)ctx->gcm_ghash;
2674d703b5cSMark Powers 
2684d703b5cSMark Powers 	if (ctx->gcm_remainder_len > 0) {
2694d703b5cSMark Powers 		uint64_t counter;
2704d703b5cSMark Powers 		uint8_t *tmpp = (uint8_t *)ctx->gcm_tmp;
2714d703b5cSMark Powers 
2724d703b5cSMark Powers 		/*
2734d703b5cSMark Powers 		 * Here is where we deal with data that is not a
2744d703b5cSMark Powers 		 * multiple of the block size.
2754d703b5cSMark Powers 		 */
2764d703b5cSMark Powers 
2774d703b5cSMark Powers 		/*
2784d703b5cSMark Powers 		 * Increment counter.
2794d703b5cSMark Powers 		 */
2804d703b5cSMark Powers 		counter = ntohll(ctx->gcm_cb[1] & counter_mask);
2814d703b5cSMark Powers 		counter = htonll(counter + 1);
2824d703b5cSMark Powers 		counter &= counter_mask;
2834d703b5cSMark Powers 		ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
2844d703b5cSMark Powers 
2854d703b5cSMark Powers 		encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb,
2864d703b5cSMark Powers 		    (uint8_t *)ctx->gcm_tmp);
2874d703b5cSMark Powers 
2884d703b5cSMark Powers 		macp = (uint8_t *)ctx->gcm_remainder;
2894d703b5cSMark Powers 		bzero(macp + ctx->gcm_remainder_len,
2904d703b5cSMark Powers 		    block_size - ctx->gcm_remainder_len);
2914d703b5cSMark Powers 
2924d703b5cSMark Powers 		/* XOR with counter block */
2934d703b5cSMark Powers 		for (i = 0; i < ctx->gcm_remainder_len; i++) {
2944d703b5cSMark Powers 			macp[i] ^= tmpp[i];
2954d703b5cSMark Powers 		}
2964d703b5cSMark Powers 
2974d703b5cSMark Powers 		/* add ciphertext to the hash */
2984d703b5cSMark Powers 		GHASH(ctx, macp, ghash);
2994d703b5cSMark Powers 
3004d703b5cSMark Powers 		ctx->gcm_processed_data_len += ctx->gcm_remainder_len;
3014d703b5cSMark Powers 	}
3024d703b5cSMark Powers 
30395014fbbSDan OpenSolaris Anderson 	ctx->gcm_len_a_len_c[1] =
30495014fbbSDan OpenSolaris Anderson 	    htonll(CRYPTO_BYTES2BITS(ctx->gcm_processed_data_len));
3054d703b5cSMark Powers 	GHASH(ctx, ctx->gcm_len_a_len_c, ghash);
3064d703b5cSMark Powers 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0,
3074d703b5cSMark Powers 	    (uint8_t *)ctx->gcm_J0);
3084d703b5cSMark Powers 	xor_block((uint8_t *)ctx->gcm_J0, ghash);
3094d703b5cSMark Powers 
3104d703b5cSMark Powers 	if (ctx->gcm_remainder_len > 0) {
3114d703b5cSMark Powers 		rv = crypto_put_output_data(macp, out, ctx->gcm_remainder_len);
3124d703b5cSMark Powers 		if (rv != CRYPTO_SUCCESS)
3134d703b5cSMark Powers 			return (rv);
3144d703b5cSMark Powers 	}
3154d703b5cSMark Powers 	out->cd_offset += ctx->gcm_remainder_len;
3164d703b5cSMark Powers 	ctx->gcm_remainder_len = 0;
3174d703b5cSMark Powers 	rv = crypto_put_output_data(ghash, out, ctx->gcm_tag_len);
3184d703b5cSMark Powers 	if (rv != CRYPTO_SUCCESS)
3194d703b5cSMark Powers 		return (rv);
3204d703b5cSMark Powers 	out->cd_offset += ctx->gcm_tag_len;
3214d703b5cSMark Powers 
3224d703b5cSMark Powers 	return (CRYPTO_SUCCESS);
3234d703b5cSMark Powers }
3244d703b5cSMark Powers 
3254d703b5cSMark Powers /*
3264d703b5cSMark Powers  * This will only deal with decrypting the last block of the input that
3274d703b5cSMark Powers  * might not be a multiple of block length.
3284d703b5cSMark Powers  */
3294d703b5cSMark Powers 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 *))3304d703b5cSMark Powers gcm_decrypt_incomplete_block(gcm_ctx_t *ctx, size_t block_size, size_t index,
3314d703b5cSMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
3324d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
3334d703b5cSMark Powers {
3344d703b5cSMark Powers 	uint8_t *datap, *outp, *counterp;
3354d703b5cSMark Powers 	uint64_t counter;
3364d703b5cSMark Powers 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
3374d703b5cSMark Powers 	int i;
3384d703b5cSMark Powers 
3394d703b5cSMark Powers 	/*
3404d703b5cSMark Powers 	 * Increment counter.
3414d703b5cSMark Powers 	 * Counter bits are confined to the bottom 32 bits
3424d703b5cSMark Powers 	 */
3434d703b5cSMark Powers 	counter = ntohll(ctx->gcm_cb[1] & counter_mask);
3444d703b5cSMark Powers 	counter = htonll(counter + 1);
3454d703b5cSMark Powers 	counter &= counter_mask;
3464d703b5cSMark Powers 	ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
3474d703b5cSMark Powers 
3484d703b5cSMark Powers 	datap = (uint8_t *)ctx->gcm_remainder;
3494d703b5cSMark Powers 	outp = &((ctx->gcm_pt_buf)[index]);
3504d703b5cSMark Powers 	counterp = (uint8_t *)ctx->gcm_tmp;
3514d703b5cSMark Powers 
3524d703b5cSMark Powers 	/* authentication tag */
3534d703b5cSMark Powers 	bzero((uint8_t *)ctx->gcm_tmp, block_size);
3544d703b5cSMark Powers 	bcopy(datap, (uint8_t *)ctx->gcm_tmp, ctx->gcm_remainder_len);
3554d703b5cSMark Powers 
3564d703b5cSMark Powers 	/* add ciphertext to the hash */
3574d703b5cSMark Powers 	GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash);
3584d703b5cSMark Powers 
3594d703b5cSMark Powers 	/* decrypt remaining ciphertext */
3604d703b5cSMark Powers 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, counterp);
3614d703b5cSMark Powers 
3624d703b5cSMark Powers 	/* XOR with counter block */
3634d703b5cSMark Powers 	for (i = 0; i < ctx->gcm_remainder_len; i++) {
3644d703b5cSMark Powers 		outp[i] = datap[i] ^ counterp[i];
3654d703b5cSMark Powers 	}
3664d703b5cSMark Powers }
3674d703b5cSMark Powers 
3684d703b5cSMark Powers /* ARGSUSED */
3694d703b5cSMark Powers 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 *))3704d703b5cSMark Powers gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length,
3714d703b5cSMark Powers     crypto_data_t *out, size_t block_size,
3724d703b5cSMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
3734d703b5cSMark Powers     void (*copy_block)(uint8_t *, uint8_t *),
3744d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
3754d703b5cSMark Powers {
3764d703b5cSMark Powers 	size_t new_len;
3774d703b5cSMark Powers 	uint8_t *new;
3784d703b5cSMark Powers 
3794d703b5cSMark Powers 	/*
3804d703b5cSMark Powers 	 * Copy contiguous ciphertext input blocks to plaintext buffer.
3814d703b5cSMark Powers 	 * Ciphertext will be decrypted in the final.
3824d703b5cSMark Powers 	 */
3834d703b5cSMark Powers 	if (length > 0) {
3844d703b5cSMark Powers 		new_len = ctx->gcm_pt_buf_len + length;
3854d703b5cSMark Powers #ifdef _KERNEL
3864d703b5cSMark Powers 		new = kmem_alloc(new_len, ctx->gcm_kmflag);
3874d703b5cSMark Powers 		bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len);
3884d703b5cSMark Powers 		kmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len);
3894d703b5cSMark Powers #else
3904d703b5cSMark Powers 		new = malloc(new_len);
3914d703b5cSMark Powers 		bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len);
3924d703b5cSMark Powers 		free(ctx->gcm_pt_buf);
3934d703b5cSMark Powers #endif
3944d703b5cSMark Powers 		if (new == NULL)
3954d703b5cSMark Powers 			return (CRYPTO_HOST_MEMORY);
3964d703b5cSMark Powers 
3974d703b5cSMark Powers 		ctx->gcm_pt_buf = new;
3984d703b5cSMark Powers 		ctx->gcm_pt_buf_len = new_len;
3994d703b5cSMark Powers 		bcopy(data, &ctx->gcm_pt_buf[ctx->gcm_processed_data_len],
4004d703b5cSMark Powers 		    length);
4014d703b5cSMark Powers 		ctx->gcm_processed_data_len += length;
4024d703b5cSMark Powers 	}
4034d703b5cSMark Powers 
4044d703b5cSMark Powers 	ctx->gcm_remainder_len = 0;
4054d703b5cSMark Powers 	return (CRYPTO_SUCCESS);
4064d703b5cSMark Powers }
4074d703b5cSMark Powers 
4084d703b5cSMark Powers 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 *))4094d703b5cSMark Powers gcm_decrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size,
4104d703b5cSMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
4114d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
4124d703b5cSMark Powers {
4134d703b5cSMark Powers 	size_t pt_len;
4144d703b5cSMark Powers 	size_t remainder;
4154d703b5cSMark Powers 	uint8_t *ghash;
4164d703b5cSMark Powers 	uint8_t *blockp;
4174d703b5cSMark Powers 	uint8_t *cbp;
4184d703b5cSMark Powers 	uint64_t counter;
4194d703b5cSMark Powers 	uint64_t counter_mask = ntohll(0x00000000ffffffffULL);
4204d703b5cSMark Powers 	int processed = 0, rv;
4214d703b5cSMark Powers 
422*fb261280SJason King 	ASSERT3U(ctx->gcm_processed_data_len, ==, ctx->gcm_pt_buf_len);
4234d703b5cSMark Powers 
4244d703b5cSMark Powers 	pt_len = ctx->gcm_processed_data_len - ctx->gcm_tag_len;
4254d703b5cSMark Powers 	ghash = (uint8_t *)ctx->gcm_ghash;
4264d703b5cSMark Powers 	blockp = ctx->gcm_pt_buf;
4274d703b5cSMark Powers 	remainder = pt_len;
4284d703b5cSMark Powers 	while (remainder > 0) {
429553d52d4SMark Fenwick 		/* Incomplete last block */
430553d52d4SMark Fenwick 		if (remainder < block_size) {
431553d52d4SMark Fenwick 			bcopy(blockp, ctx->gcm_remainder, remainder);
432553d52d4SMark Fenwick 			ctx->gcm_remainder_len = remainder;
433553d52d4SMark Fenwick 			/*
434553d52d4SMark Fenwick 			 * not expecting anymore ciphertext, just
435553d52d4SMark Fenwick 			 * compute plaintext for the remaining input
436553d52d4SMark Fenwick 			 */
437553d52d4SMark Fenwick 			gcm_decrypt_incomplete_block(ctx, block_size,
438553d52d4SMark Fenwick 			    processed, encrypt_block, xor_block);
439553d52d4SMark Fenwick 			ctx->gcm_remainder_len = 0;
440553d52d4SMark Fenwick 			goto out;
441553d52d4SMark Fenwick 		}
4424d703b5cSMark Powers 		/* add ciphertext to the hash */
4434d703b5cSMark Powers 		GHASH(ctx, blockp, ghash);
4444d703b5cSMark Powers 
4454d703b5cSMark Powers 		/*
4464d703b5cSMark Powers 		 * Increment counter.
4474d703b5cSMark Powers 		 * Counter bits are confined to the bottom 32 bits
4484d703b5cSMark Powers 		 */
4494d703b5cSMark Powers 		counter = ntohll(ctx->gcm_cb[1] & counter_mask);
4504d703b5cSMark Powers 		counter = htonll(counter + 1);
4514d703b5cSMark Powers 		counter &= counter_mask;
4524d703b5cSMark Powers 		ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter;
4534d703b5cSMark Powers 
4544d703b5cSMark Powers 		cbp = (uint8_t *)ctx->gcm_tmp;
4554d703b5cSMark Powers 		encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, cbp);
4564d703b5cSMark Powers 
4574d703b5cSMark Powers 		/* XOR with ciphertext */
4584d703b5cSMark Powers 		xor_block(cbp, blockp);
4594d703b5cSMark Powers 
4604d703b5cSMark Powers 		processed += block_size;
4614d703b5cSMark Powers 		blockp += block_size;
4624d703b5cSMark Powers 		remainder -= block_size;
4634d703b5cSMark Powers 	}
4644d703b5cSMark Powers out:
46595014fbbSDan OpenSolaris Anderson 	ctx->gcm_len_a_len_c[1] = htonll(CRYPTO_BYTES2BITS(pt_len));
4664d703b5cSMark Powers 	GHASH(ctx, ctx->gcm_len_a_len_c, ghash);
4674d703b5cSMark Powers 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0,
4684d703b5cSMark Powers 	    (uint8_t *)ctx->gcm_J0);
4694d703b5cSMark Powers 	xor_block((uint8_t *)ctx->gcm_J0, ghash);
4704d703b5cSMark Powers 
4714d703b5cSMark Powers 	/* compare the input authentication tag with what we calculated */
4724d703b5cSMark Powers 	if (bcmp(&ctx->gcm_pt_buf[pt_len], ghash, ctx->gcm_tag_len)) {
4734d703b5cSMark Powers 		/* They don't match */
4744d703b5cSMark Powers 		return (CRYPTO_INVALID_MAC);
4754d703b5cSMark Powers 	} else {
4764d703b5cSMark Powers 		rv = crypto_put_output_data(ctx->gcm_pt_buf, out, pt_len);
4774d703b5cSMark Powers 		if (rv != CRYPTO_SUCCESS)
4784d703b5cSMark Powers 			return (rv);
4794d703b5cSMark Powers 		out->cd_offset += pt_len;
4804d703b5cSMark Powers 	}
4814d703b5cSMark Powers 	return (CRYPTO_SUCCESS);
4824d703b5cSMark Powers }
4834d703b5cSMark Powers 
4844d703b5cSMark Powers static int
gcm_validate_args(CK_AES_GCM_PARAMS * gcm_param)4854d703b5cSMark Powers gcm_validate_args(CK_AES_GCM_PARAMS *gcm_param)
4864d703b5cSMark Powers {
4874d703b5cSMark Powers 	size_t tag_len;
4884d703b5cSMark Powers 
4894d703b5cSMark Powers 	/*
4904d703b5cSMark Powers 	 * Check the length of the authentication tag (in bits).
4914d703b5cSMark Powers 	 */
4924d703b5cSMark Powers 	tag_len = gcm_param->ulTagBits;
4934d703b5cSMark Powers 	switch (tag_len) {
4944d703b5cSMark Powers 	case 32:
4954d703b5cSMark Powers 	case 64:
4964d703b5cSMark Powers 	case 96:
4974d703b5cSMark Powers 	case 104:
4984d703b5cSMark Powers 	case 112:
4994d703b5cSMark Powers 	case 120:
5004d703b5cSMark Powers 	case 128:
5014d703b5cSMark Powers 		break;
5024d703b5cSMark Powers 	default:
5034d703b5cSMark Powers 		return (CRYPTO_MECHANISM_PARAM_INVALID);
5044d703b5cSMark Powers 	}
5054d703b5cSMark Powers 
5064d703b5cSMark Powers 	if (gcm_param->ulIvLen == 0)
5074d703b5cSMark Powers 		return (CRYPTO_MECHANISM_PARAM_INVALID);
5084d703b5cSMark Powers 
5094d703b5cSMark Powers 	return (CRYPTO_SUCCESS);
5104d703b5cSMark Powers }
5114d703b5cSMark Powers 
5124d703b5cSMark Powers 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 *))5134d703b5cSMark Powers gcm_format_initial_blocks(uchar_t *iv, ulong_t iv_len,
5144d703b5cSMark Powers     gcm_ctx_t *ctx, size_t block_size,
5154d703b5cSMark Powers     void (*copy_block)(uint8_t *, uint8_t *),
5164d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
5174d703b5cSMark Powers {
5184d703b5cSMark Powers 	uint8_t *cb;
5194d703b5cSMark Powers 	ulong_t remainder = iv_len;
5204d703b5cSMark Powers 	ulong_t processed = 0;
5214d703b5cSMark Powers 	uint8_t *datap, *ghash;
5224d703b5cSMark Powers 	uint64_t len_a_len_c[2];
5234d703b5cSMark Powers 
5244d703b5cSMark Powers 	ghash = (uint8_t *)ctx->gcm_ghash;
5254d703b5cSMark Powers 	cb = (uint8_t *)ctx->gcm_cb;
5264d703b5cSMark Powers 	if (iv_len == 12) {
5274d703b5cSMark Powers 		bcopy(iv, cb, 12);
5284d703b5cSMark Powers 		cb[12] = 0;
5294d703b5cSMark Powers 		cb[13] = 0;
5304d703b5cSMark Powers 		cb[14] = 0;
5314d703b5cSMark Powers 		cb[15] = 1;
5324d703b5cSMark Powers 		/* J0 will be used again in the final */
5334d703b5cSMark Powers 		copy_block(cb, (uint8_t *)ctx->gcm_J0);
5344d703b5cSMark Powers 	} else {
5354d703b5cSMark Powers 		/* GHASH the IV */
5364d703b5cSMark Powers 		do {
5374d703b5cSMark Powers 			if (remainder < block_size) {
5384d703b5cSMark Powers 				bzero(cb, block_size);
5394d703b5cSMark Powers 				bcopy(&(iv[processed]), cb, remainder);
5404d703b5cSMark Powers 				datap = (uint8_t *)cb;
5414d703b5cSMark Powers 				remainder = 0;
5424d703b5cSMark Powers 			} else {
5434d703b5cSMark Powers 				datap = (uint8_t *)(&(iv[processed]));
5444d703b5cSMark Powers 				processed += block_size;
5454d703b5cSMark Powers 				remainder -= block_size;
5464d703b5cSMark Powers 			}
5474d703b5cSMark Powers 			GHASH(ctx, datap, ghash);
5484d703b5cSMark Powers 		} while (remainder > 0);
5494d703b5cSMark Powers 
5504d703b5cSMark Powers 		len_a_len_c[0] = 0;
55195014fbbSDan OpenSolaris Anderson 		len_a_len_c[1] = htonll(CRYPTO_BYTES2BITS(iv_len));
5524d703b5cSMark Powers 		GHASH(ctx, len_a_len_c, ctx->gcm_J0);
5534d703b5cSMark Powers 
5544d703b5cSMark Powers 		/* J0 will be used again in the final */
5554d703b5cSMark Powers 		copy_block((uint8_t *)ctx->gcm_J0, (uint8_t *)cb);
5564d703b5cSMark Powers 	}
5574d703b5cSMark Powers }
5584d703b5cSMark Powers 
5594d703b5cSMark Powers /*
5604d703b5cSMark Powers  * The following function is called at encrypt or decrypt init time
5614d703b5cSMark Powers  * for AES GCM mode.
5624d703b5cSMark Powers  */
5634d703b5cSMark Powers 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 *))5644d703b5cSMark Powers gcm_init(gcm_ctx_t *ctx, unsigned char *iv, size_t iv_len,
5654d703b5cSMark Powers     unsigned char *auth_data, size_t auth_data_len, size_t block_size,
5664d703b5cSMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
5674d703b5cSMark Powers     void (*copy_block)(uint8_t *, uint8_t *),
5684d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
5694d703b5cSMark Powers {
5704d703b5cSMark Powers 	uint8_t *ghash, *datap, *authp;
5714d703b5cSMark Powers 	size_t remainder, processed;
5724d703b5cSMark Powers 
5734d703b5cSMark Powers 	/* encrypt zero block to get subkey H */
5744d703b5cSMark Powers 	bzero(ctx->gcm_H, sizeof (ctx->gcm_H));
5754d703b5cSMark Powers 	encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_H,
5764d703b5cSMark Powers 	    (uint8_t *)ctx->gcm_H);
5774d703b5cSMark Powers 
5784d703b5cSMark Powers 	gcm_format_initial_blocks(iv, iv_len, ctx, block_size,
5794d703b5cSMark Powers 	    copy_block, xor_block);
5804d703b5cSMark Powers 
5814d703b5cSMark Powers 	authp = (uint8_t *)ctx->gcm_tmp;
5824d703b5cSMark Powers 	ghash = (uint8_t *)ctx->gcm_ghash;
5834d703b5cSMark Powers 	bzero(authp, block_size);
5844d703b5cSMark Powers 	bzero(ghash, block_size);
5854d703b5cSMark Powers 
5864d703b5cSMark Powers 	processed = 0;
5874d703b5cSMark Powers 	remainder = auth_data_len;
5884d703b5cSMark Powers 	do {
5894d703b5cSMark Powers 		if (remainder < block_size) {
5904d703b5cSMark Powers 			/*
5914d703b5cSMark Powers 			 * There's not a block full of data, pad rest of
5924d703b5cSMark Powers 			 * buffer with zero
5934d703b5cSMark Powers 			 */
5944d703b5cSMark Powers 			bzero(authp, block_size);
5954d703b5cSMark Powers 			bcopy(&(auth_data[processed]), authp, remainder);
5964d703b5cSMark Powers 			datap = (uint8_t *)authp;
5974d703b5cSMark Powers 			remainder = 0;
5984d703b5cSMark Powers 		} else {
5994d703b5cSMark Powers 			datap = (uint8_t *)(&(auth_data[processed]));
6004d703b5cSMark Powers 			processed += block_size;
6014d703b5cSMark Powers 			remainder -= block_size;
6024d703b5cSMark Powers 		}
6034d703b5cSMark Powers 
6044d703b5cSMark Powers 		/* add auth data to the hash */
6054d703b5cSMark Powers 		GHASH(ctx, datap, ghash);
6064d703b5cSMark Powers 
6074d703b5cSMark Powers 	} while (remainder > 0);
6084d703b5cSMark Powers 
6094d703b5cSMark Powers 	return (CRYPTO_SUCCESS);
6104d703b5cSMark Powers }
6114d703b5cSMark Powers 
6124d703b5cSMark Powers 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 *))6134d703b5cSMark Powers gcm_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size,
6144d703b5cSMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
6154d703b5cSMark Powers     void (*copy_block)(uint8_t *, uint8_t *),
6164d703b5cSMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
6174d703b5cSMark Powers {
6184d703b5cSMark Powers 	int rv;
6194d703b5cSMark Powers 	CK_AES_GCM_PARAMS *gcm_param;
6204d703b5cSMark Powers 
6214d703b5cSMark Powers 	if (param != NULL) {
62295014fbbSDan OpenSolaris Anderson 		gcm_param = (CK_AES_GCM_PARAMS *)(void *)param;
6234d703b5cSMark Powers 
6244d703b5cSMark Powers 		if ((rv = gcm_validate_args(gcm_param)) != 0) {
6254d703b5cSMark Powers 			return (rv);
6264d703b5cSMark Powers 		}
6274d703b5cSMark Powers 
6284d703b5cSMark Powers 		gcm_ctx->gcm_tag_len = gcm_param->ulTagBits;
6294d703b5cSMark Powers 		gcm_ctx->gcm_tag_len >>= 3;
6304d703b5cSMark Powers 		gcm_ctx->gcm_processed_data_len = 0;
6314d703b5cSMark Powers 
6324d703b5cSMark Powers 		/* these values are in bits */
63395014fbbSDan OpenSolaris Anderson 		gcm_ctx->gcm_len_a_len_c[0]
63495014fbbSDan OpenSolaris Anderson 		    = htonll(CRYPTO_BYTES2BITS(gcm_param->ulAADLen));
6354d703b5cSMark Powers 
6364d703b5cSMark Powers 		rv = CRYPTO_SUCCESS;
6374d703b5cSMark Powers 		gcm_ctx->gcm_flags |= GCM_MODE;
6384d703b5cSMark Powers 	} else {
6394d703b5cSMark Powers 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
6404d703b5cSMark Powers 		goto out;
6414d703b5cSMark Powers 	}
6424d703b5cSMark Powers 
6434d703b5cSMark Powers 	if (gcm_init(gcm_ctx, gcm_param->pIv, gcm_param->ulIvLen,
6444d703b5cSMark Powers 	    gcm_param->pAAD, gcm_param->ulAADLen, block_size,
6454d703b5cSMark Powers 	    encrypt_block, copy_block, xor_block) != 0) {
6464d703b5cSMark Powers 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
6474d703b5cSMark Powers 	}
6484d703b5cSMark Powers out:
6494d703b5cSMark Powers 	return (rv);
6504d703b5cSMark Powers }
6514d703b5cSMark Powers 
652983a1033SMark Powers 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 *))653983a1033SMark Powers gmac_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size,
654983a1033SMark Powers     int (*encrypt_block)(const void *, const uint8_t *, uint8_t *),
655983a1033SMark Powers     void (*copy_block)(uint8_t *, uint8_t *),
656983a1033SMark Powers     void (*xor_block)(uint8_t *, uint8_t *))
657983a1033SMark Powers {
658983a1033SMark Powers 	int rv;
659983a1033SMark Powers 	CK_AES_GMAC_PARAMS *gmac_param;
660983a1033SMark Powers 
661983a1033SMark Powers 	if (param != NULL) {
66295014fbbSDan OpenSolaris Anderson 		gmac_param = (CK_AES_GMAC_PARAMS *)(void *)param;
663983a1033SMark Powers 
664983a1033SMark Powers 		gcm_ctx->gcm_tag_len = CRYPTO_BITS2BYTES(AES_GMAC_TAG_BITS);
665983a1033SMark Powers 		gcm_ctx->gcm_processed_data_len = 0;
666983a1033SMark Powers 
667983a1033SMark Powers 		/* these values are in bits */
66895014fbbSDan OpenSolaris Anderson 		gcm_ctx->gcm_len_a_len_c[0]
66995014fbbSDan OpenSolaris Anderson 		    = htonll(CRYPTO_BYTES2BITS(gmac_param->ulAADLen));
670983a1033SMark Powers 
671983a1033SMark Powers 		rv = CRYPTO_SUCCESS;
672983a1033SMark Powers 		gcm_ctx->gcm_flags |= GMAC_MODE;
673983a1033SMark Powers 	} else {
674983a1033SMark Powers 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
675983a1033SMark Powers 		goto out;
676983a1033SMark Powers 	}
677983a1033SMark Powers 
678983a1033SMark Powers 	if (gcm_init(gcm_ctx, gmac_param->pIv, AES_GMAC_IV_LEN,
679983a1033SMark Powers 	    gmac_param->pAAD, gmac_param->ulAADLen, block_size,
680983a1033SMark Powers 	    encrypt_block, copy_block, xor_block) != 0) {
681983a1033SMark Powers 		rv = CRYPTO_MECHANISM_PARAM_INVALID;
682983a1033SMark Powers 	}
683983a1033SMark Powers out:
684983a1033SMark Powers 	return (rv);
685983a1033SMark Powers }
686983a1033SMark Powers 
6874d703b5cSMark Powers void *
gcm_alloc_ctx(int kmflag)6884d703b5cSMark Powers gcm_alloc_ctx(int kmflag)
6894d703b5cSMark Powers {
6904d703b5cSMark Powers 	gcm_ctx_t *gcm_ctx;
6914d703b5cSMark Powers 
6924d703b5cSMark Powers #ifdef _KERNEL
6934d703b5cSMark Powers 	if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL)
6944d703b5cSMark Powers #else
6954d703b5cSMark Powers 	if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL)
6964d703b5cSMark Powers #endif
6974d703b5cSMark Powers 		return (NULL);
6984d703b5cSMark Powers 
6994d703b5cSMark Powers 	gcm_ctx->gcm_flags = GCM_MODE;
7004d703b5cSMark Powers 	return (gcm_ctx);
7014d703b5cSMark Powers }
7024d703b5cSMark Powers 
703983a1033SMark Powers void *
gmac_alloc_ctx(int kmflag)704983a1033SMark Powers gmac_alloc_ctx(int kmflag)
705983a1033SMark Powers {
706983a1033SMark Powers 	gcm_ctx_t *gcm_ctx;
707983a1033SMark Powers 
708983a1033SMark Powers #ifdef _KERNEL
709983a1033SMark Powers 	if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL)
710983a1033SMark Powers #else
711983a1033SMark Powers 	if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL)
712983a1033SMark Powers #endif
713983a1033SMark Powers 		return (NULL);
714983a1033SMark Powers 
715983a1033SMark Powers 	gcm_ctx->gcm_flags = GMAC_MODE;
716983a1033SMark Powers 	return (gcm_ctx);
717983a1033SMark Powers }
718983a1033SMark Powers 
7194d703b5cSMark Powers void
gcm_set_kmflag(gcm_ctx_t * ctx,int kmflag)7204d703b5cSMark Powers gcm_set_kmflag(gcm_ctx_t *ctx, int kmflag)
7214d703b5cSMark Powers {
7224d703b5cSMark Powers 	ctx->gcm_kmflag = kmflag;
7234d703b5cSMark Powers }
724104d3bdeSDan OpenSolaris Anderson 
725104d3bdeSDan OpenSolaris Anderson 
726104d3bdeSDan OpenSolaris Anderson #ifdef __amd64
727104d3bdeSDan OpenSolaris Anderson /*
728104d3bdeSDan OpenSolaris Anderson  * Return 1 if executing on Intel with PCLMULQDQ instructions,
729104d3bdeSDan OpenSolaris Anderson  * otherwise 0 (i.e., Intel without PCLMULQDQ or AMD64).
730104d3bdeSDan OpenSolaris Anderson  * Cache the result, as the CPU can't change.
731104d3bdeSDan OpenSolaris Anderson  *
732104d3bdeSDan OpenSolaris Anderson  * Note: the userland version uses getisax().  The kernel version uses
7337417cfdeSKuriakose Kuruvilla  * is_x86_featureset().
734104d3bdeSDan OpenSolaris Anderson  */
735104d3bdeSDan OpenSolaris Anderson static int
intel_pclmulqdq_instruction_present(void)736104d3bdeSDan OpenSolaris Anderson intel_pclmulqdq_instruction_present(void)
737104d3bdeSDan OpenSolaris Anderson {
738104d3bdeSDan OpenSolaris Anderson 	static int	cached_result = -1;
739104d3bdeSDan OpenSolaris Anderson 
740104d3bdeSDan OpenSolaris Anderson 	if (cached_result == -1) { /* first time */
741104d3bdeSDan OpenSolaris Anderson #ifdef _KERNEL
7427417cfdeSKuriakose Kuruvilla 		cached_result =
7437417cfdeSKuriakose Kuruvilla 		    is_x86_feature(x86_featureset, X86FSET_PCLMULQDQ);
744104d3bdeSDan OpenSolaris Anderson #else
745104d3bdeSDan OpenSolaris Anderson 		uint_t		ui = 0;
746104d3bdeSDan OpenSolaris Anderson 
747104d3bdeSDan OpenSolaris Anderson 		(void) getisax(&ui, 1);
748104d3bdeSDan OpenSolaris Anderson 		cached_result = (ui & AV_386_PCLMULQDQ) != 0;
749104d3bdeSDan OpenSolaris Anderson #endif	/* _KERNEL */
750104d3bdeSDan OpenSolaris Anderson 	}
751104d3bdeSDan OpenSolaris Anderson 
752104d3bdeSDan OpenSolaris Anderson 	return (cached_result);
753104d3bdeSDan OpenSolaris Anderson }
754104d3bdeSDan OpenSolaris Anderson #endif	/* __amd64 */
755