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 /* 22*7417cfdeSKuriakose Kuruvilla * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 234d703b5cSMark Powers */ 244d703b5cSMark Powers 25104d3bdeSDan OpenSolaris Anderson 264d703b5cSMark Powers #ifndef _KERNEL 274d703b5cSMark Powers #include <strings.h> 284d703b5cSMark Powers #include <limits.h> 294d703b5cSMark Powers #include <assert.h> 304d703b5cSMark Powers #include <security/cryptoki.h> 31104d3bdeSDan OpenSolaris Anderson #endif /* _KERNEL */ 32104d3bdeSDan OpenSolaris Anderson 334d703b5cSMark Powers 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 */ 45*7417cfdeSKuriakose 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 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 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 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 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 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 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 4224d703b5cSMark Powers ASSERT(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 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 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 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 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 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 * 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 * 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 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 733*7417cfdeSKuriakose Kuruvilla * is_x86_featureset(). 734104d3bdeSDan OpenSolaris Anderson */ 735104d3bdeSDan OpenSolaris Anderson static int 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 742*7417cfdeSKuriakose Kuruvilla cached_result = 743*7417cfdeSKuriakose 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