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