18005SMark.Powers@Sun.COM /* 28005SMark.Powers@Sun.COM * CDDL HEADER START 38005SMark.Powers@Sun.COM * 48005SMark.Powers@Sun.COM * The contents of this file are subject to the terms of the 58005SMark.Powers@Sun.COM * Common Development and Distribution License (the "License"). 68005SMark.Powers@Sun.COM * You may not use this file except in compliance with the License. 78005SMark.Powers@Sun.COM * 88005SMark.Powers@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98005SMark.Powers@Sun.COM * or http://www.opensolaris.org/os/licensing. 108005SMark.Powers@Sun.COM * See the License for the specific language governing permissions 118005SMark.Powers@Sun.COM * and limitations under the License. 128005SMark.Powers@Sun.COM * 138005SMark.Powers@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 148005SMark.Powers@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158005SMark.Powers@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 168005SMark.Powers@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 178005SMark.Powers@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 188005SMark.Powers@Sun.COM * 198005SMark.Powers@Sun.COM * CDDL HEADER END 208005SMark.Powers@Sun.COM */ 218005SMark.Powers@Sun.COM /* 22*11413Sopensolaris@drydog.com * Copyright 2010 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 4311299Sopensolaris@drydog.com 4411299Sopensolaris@drydog.com #ifdef _KERNEL 4511299Sopensolaris@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); \ 123*11413Sopensolaris@drydog.com gcm_mul((uint64_t *)(void *)(c)->gcm_ghash, (c)->gcm_H, \ 124*11413Sopensolaris@drydog.com (uint64_t *)(void *)(t)); 125*11413Sopensolaris@drydog.com 1268005SMark.Powers@Sun.COM 1278005SMark.Powers@Sun.COM /* 1288005SMark.Powers@Sun.COM * Encrypt multiple blocks of data in GCM mode. Decrypt for GCM mode 1298005SMark.Powers@Sun.COM * is done in another function. 1308005SMark.Powers@Sun.COM */ 1318005SMark.Powers@Sun.COM int 1328005SMark.Powers@Sun.COM gcm_mode_encrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 1338005SMark.Powers@Sun.COM crypto_data_t *out, size_t block_size, 1348005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 1358005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 1368005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 1378005SMark.Powers@Sun.COM { 1388005SMark.Powers@Sun.COM size_t remainder = length; 1398005SMark.Powers@Sun.COM size_t need; 1408005SMark.Powers@Sun.COM uint8_t *datap = (uint8_t *)data; 1418005SMark.Powers@Sun.COM uint8_t *blockp; 1428005SMark.Powers@Sun.COM uint8_t *lastp; 1438005SMark.Powers@Sun.COM void *iov_or_mp; 1448005SMark.Powers@Sun.COM offset_t offset; 1458005SMark.Powers@Sun.COM uint8_t *out_data_1; 1468005SMark.Powers@Sun.COM uint8_t *out_data_2; 1478005SMark.Powers@Sun.COM size_t out_data_1_len; 1488005SMark.Powers@Sun.COM uint64_t counter; 1498005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 1508005SMark.Powers@Sun.COM 1518005SMark.Powers@Sun.COM if (length + ctx->gcm_remainder_len < block_size) { 1528005SMark.Powers@Sun.COM /* accumulate bytes here and return */ 1538005SMark.Powers@Sun.COM bcopy(datap, 1548005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_remainder + ctx->gcm_remainder_len, 1558005SMark.Powers@Sun.COM length); 1568005SMark.Powers@Sun.COM ctx->gcm_remainder_len += length; 1578005SMark.Powers@Sun.COM ctx->gcm_copy_to = datap; 1588005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 1598005SMark.Powers@Sun.COM } 1608005SMark.Powers@Sun.COM 1618005SMark.Powers@Sun.COM lastp = (uint8_t *)ctx->gcm_cb; 1628005SMark.Powers@Sun.COM if (out != NULL) 1638005SMark.Powers@Sun.COM crypto_init_ptrs(out, &iov_or_mp, &offset); 1648005SMark.Powers@Sun.COM 1658005SMark.Powers@Sun.COM do { 1668005SMark.Powers@Sun.COM /* Unprocessed data from last call. */ 1678005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 1688005SMark.Powers@Sun.COM need = block_size - ctx->gcm_remainder_len; 1698005SMark.Powers@Sun.COM 1708005SMark.Powers@Sun.COM if (need > remainder) 1718005SMark.Powers@Sun.COM return (CRYPTO_DATA_LEN_RANGE); 1728005SMark.Powers@Sun.COM 1738005SMark.Powers@Sun.COM bcopy(datap, &((uint8_t *)ctx->gcm_remainder) 1748005SMark.Powers@Sun.COM [ctx->gcm_remainder_len], need); 1758005SMark.Powers@Sun.COM 1768005SMark.Powers@Sun.COM blockp = (uint8_t *)ctx->gcm_remainder; 1778005SMark.Powers@Sun.COM } else { 1788005SMark.Powers@Sun.COM blockp = datap; 1798005SMark.Powers@Sun.COM } 1808005SMark.Powers@Sun.COM 1818005SMark.Powers@Sun.COM /* 1828005SMark.Powers@Sun.COM * Increment counter. Counter bits are confined 1838005SMark.Powers@Sun.COM * to the bottom 32 bits of the counter block. 1848005SMark.Powers@Sun.COM */ 1858005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 1868005SMark.Powers@Sun.COM counter = htonll(counter + 1); 1878005SMark.Powers@Sun.COM counter &= counter_mask; 1888005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 1898005SMark.Powers@Sun.COM 1908005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 1918005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_tmp); 1928005SMark.Powers@Sun.COM xor_block(blockp, (uint8_t *)ctx->gcm_tmp); 1938005SMark.Powers@Sun.COM 1948005SMark.Powers@Sun.COM lastp = (uint8_t *)ctx->gcm_tmp; 1958005SMark.Powers@Sun.COM 1968005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += block_size; 1978005SMark.Powers@Sun.COM 1988005SMark.Powers@Sun.COM if (out == NULL) { 1998005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 2008005SMark.Powers@Sun.COM bcopy(blockp, ctx->gcm_copy_to, 2018005SMark.Powers@Sun.COM ctx->gcm_remainder_len); 2028005SMark.Powers@Sun.COM bcopy(blockp + ctx->gcm_remainder_len, datap, 2038005SMark.Powers@Sun.COM need); 2048005SMark.Powers@Sun.COM } 2058005SMark.Powers@Sun.COM } else { 2068005SMark.Powers@Sun.COM crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 2078005SMark.Powers@Sun.COM &out_data_1_len, &out_data_2, block_size); 2088005SMark.Powers@Sun.COM 2098005SMark.Powers@Sun.COM /* copy block to where it belongs */ 2108005SMark.Powers@Sun.COM if (out_data_1_len == block_size) { 2118005SMark.Powers@Sun.COM copy_block(lastp, out_data_1); 2128005SMark.Powers@Sun.COM } else { 2138005SMark.Powers@Sun.COM bcopy(lastp, out_data_1, out_data_1_len); 2148005SMark.Powers@Sun.COM if (out_data_2 != NULL) { 2158005SMark.Powers@Sun.COM bcopy(lastp + out_data_1_len, 2168005SMark.Powers@Sun.COM out_data_2, 2178005SMark.Powers@Sun.COM block_size - out_data_1_len); 2188005SMark.Powers@Sun.COM } 2198005SMark.Powers@Sun.COM } 2208005SMark.Powers@Sun.COM /* update offset */ 2218005SMark.Powers@Sun.COM out->cd_offset += block_size; 2228005SMark.Powers@Sun.COM } 2238005SMark.Powers@Sun.COM 2248005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 2258005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 2268005SMark.Powers@Sun.COM 2278005SMark.Powers@Sun.COM /* Update pointer to next block of data to be processed. */ 2288005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len != 0) { 2298005SMark.Powers@Sun.COM datap += need; 2308005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 2318005SMark.Powers@Sun.COM } else { 2328005SMark.Powers@Sun.COM datap += block_size; 2338005SMark.Powers@Sun.COM } 2348005SMark.Powers@Sun.COM 2358005SMark.Powers@Sun.COM remainder = (size_t)&data[length] - (size_t)datap; 2368005SMark.Powers@Sun.COM 2378005SMark.Powers@Sun.COM /* Incomplete last block. */ 2388005SMark.Powers@Sun.COM if (remainder > 0 && remainder < block_size) { 2398005SMark.Powers@Sun.COM bcopy(datap, ctx->gcm_remainder, remainder); 2408005SMark.Powers@Sun.COM ctx->gcm_remainder_len = remainder; 2418005SMark.Powers@Sun.COM ctx->gcm_copy_to = datap; 2428005SMark.Powers@Sun.COM goto out; 2438005SMark.Powers@Sun.COM } 2448005SMark.Powers@Sun.COM ctx->gcm_copy_to = NULL; 2458005SMark.Powers@Sun.COM 2468005SMark.Powers@Sun.COM } while (remainder > 0); 2478005SMark.Powers@Sun.COM out: 2488005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 2498005SMark.Powers@Sun.COM } 2508005SMark.Powers@Sun.COM 2518005SMark.Powers@Sun.COM /* ARGSUSED */ 2528005SMark.Powers@Sun.COM int 2538005SMark.Powers@Sun.COM gcm_encrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 2548005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 2558005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 2568005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 2578005SMark.Powers@Sun.COM { 2588005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 2598005SMark.Powers@Sun.COM uint8_t *ghash, *macp; 2608005SMark.Powers@Sun.COM int i, rv; 2618005SMark.Powers@Sun.COM 2628005SMark.Powers@Sun.COM if (out->cd_length < 2638005SMark.Powers@Sun.COM (ctx->gcm_remainder_len + ctx->gcm_tag_len)) { 2648005SMark.Powers@Sun.COM return (CRYPTO_DATA_LEN_RANGE); 2658005SMark.Powers@Sun.COM } 2668005SMark.Powers@Sun.COM 2678005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 2688005SMark.Powers@Sun.COM 2698005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 2708005SMark.Powers@Sun.COM uint64_t counter; 2718005SMark.Powers@Sun.COM uint8_t *tmpp = (uint8_t *)ctx->gcm_tmp; 2728005SMark.Powers@Sun.COM 2738005SMark.Powers@Sun.COM /* 2748005SMark.Powers@Sun.COM * Here is where we deal with data that is not a 2758005SMark.Powers@Sun.COM * multiple of the block size. 2768005SMark.Powers@Sun.COM */ 2778005SMark.Powers@Sun.COM 2788005SMark.Powers@Sun.COM /* 2798005SMark.Powers@Sun.COM * Increment counter. 2808005SMark.Powers@Sun.COM */ 2818005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 2828005SMark.Powers@Sun.COM counter = htonll(counter + 1); 2838005SMark.Powers@Sun.COM counter &= counter_mask; 2848005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 2858005SMark.Powers@Sun.COM 2868005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 2878005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_tmp); 2888005SMark.Powers@Sun.COM 2898005SMark.Powers@Sun.COM macp = (uint8_t *)ctx->gcm_remainder; 2908005SMark.Powers@Sun.COM bzero(macp + ctx->gcm_remainder_len, 2918005SMark.Powers@Sun.COM block_size - ctx->gcm_remainder_len); 2928005SMark.Powers@Sun.COM 2938005SMark.Powers@Sun.COM /* XOR with counter block */ 2948005SMark.Powers@Sun.COM for (i = 0; i < ctx->gcm_remainder_len; i++) { 2958005SMark.Powers@Sun.COM macp[i] ^= tmpp[i]; 2968005SMark.Powers@Sun.COM } 2978005SMark.Powers@Sun.COM 2988005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 2998005SMark.Powers@Sun.COM GHASH(ctx, macp, ghash); 3008005SMark.Powers@Sun.COM 3018005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += ctx->gcm_remainder_len; 3028005SMark.Powers@Sun.COM } 3038005SMark.Powers@Sun.COM 304*11413Sopensolaris@drydog.com ctx->gcm_len_a_len_c[1] = 305*11413Sopensolaris@drydog.com htonll(CRYPTO_BYTES2BITS(ctx->gcm_processed_data_len)); 3068005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 3078005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 3088005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_J0); 3098005SMark.Powers@Sun.COM xor_block((uint8_t *)ctx->gcm_J0, ghash); 3108005SMark.Powers@Sun.COM 3118005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 3128005SMark.Powers@Sun.COM rv = crypto_put_output_data(macp, out, ctx->gcm_remainder_len); 3138005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 3148005SMark.Powers@Sun.COM return (rv); 3158005SMark.Powers@Sun.COM } 3168005SMark.Powers@Sun.COM out->cd_offset += ctx->gcm_remainder_len; 3178005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 3188005SMark.Powers@Sun.COM rv = crypto_put_output_data(ghash, out, ctx->gcm_tag_len); 3198005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 3208005SMark.Powers@Sun.COM return (rv); 3218005SMark.Powers@Sun.COM out->cd_offset += ctx->gcm_tag_len; 3228005SMark.Powers@Sun.COM 3238005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 3248005SMark.Powers@Sun.COM } 3258005SMark.Powers@Sun.COM 3268005SMark.Powers@Sun.COM /* 3278005SMark.Powers@Sun.COM * This will only deal with decrypting the last block of the input that 3288005SMark.Powers@Sun.COM * might not be a multiple of block length. 3298005SMark.Powers@Sun.COM */ 3308005SMark.Powers@Sun.COM static void 3318005SMark.Powers@Sun.COM gcm_decrypt_incomplete_block(gcm_ctx_t *ctx, size_t block_size, size_t index, 3328005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 3338005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 3348005SMark.Powers@Sun.COM { 3358005SMark.Powers@Sun.COM uint8_t *datap, *outp, *counterp; 3368005SMark.Powers@Sun.COM uint64_t counter; 3378005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 3388005SMark.Powers@Sun.COM int i; 3398005SMark.Powers@Sun.COM 3408005SMark.Powers@Sun.COM /* 3418005SMark.Powers@Sun.COM * Increment counter. 3428005SMark.Powers@Sun.COM * Counter bits are confined to the bottom 32 bits 3438005SMark.Powers@Sun.COM */ 3448005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 3458005SMark.Powers@Sun.COM counter = htonll(counter + 1); 3468005SMark.Powers@Sun.COM counter &= counter_mask; 3478005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 3488005SMark.Powers@Sun.COM 3498005SMark.Powers@Sun.COM datap = (uint8_t *)ctx->gcm_remainder; 3508005SMark.Powers@Sun.COM outp = &((ctx->gcm_pt_buf)[index]); 3518005SMark.Powers@Sun.COM counterp = (uint8_t *)ctx->gcm_tmp; 3528005SMark.Powers@Sun.COM 3538005SMark.Powers@Sun.COM /* authentication tag */ 3548005SMark.Powers@Sun.COM bzero((uint8_t *)ctx->gcm_tmp, block_size); 3558005SMark.Powers@Sun.COM bcopy(datap, (uint8_t *)ctx->gcm_tmp, ctx->gcm_remainder_len); 3568005SMark.Powers@Sun.COM 3578005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 3588005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 3598005SMark.Powers@Sun.COM 3608005SMark.Powers@Sun.COM /* decrypt remaining ciphertext */ 3618005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, counterp); 3628005SMark.Powers@Sun.COM 3638005SMark.Powers@Sun.COM /* XOR with counter block */ 3648005SMark.Powers@Sun.COM for (i = 0; i < ctx->gcm_remainder_len; i++) { 3658005SMark.Powers@Sun.COM outp[i] = datap[i] ^ counterp[i]; 3668005SMark.Powers@Sun.COM } 3678005SMark.Powers@Sun.COM } 3688005SMark.Powers@Sun.COM 3698005SMark.Powers@Sun.COM /* ARGSUSED */ 3708005SMark.Powers@Sun.COM int 3718005SMark.Powers@Sun.COM gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 3728005SMark.Powers@Sun.COM crypto_data_t *out, size_t block_size, 3738005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 3748005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 3758005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 3768005SMark.Powers@Sun.COM { 3778005SMark.Powers@Sun.COM size_t new_len; 3788005SMark.Powers@Sun.COM uint8_t *new; 3798005SMark.Powers@Sun.COM 3808005SMark.Powers@Sun.COM /* 3818005SMark.Powers@Sun.COM * Copy contiguous ciphertext input blocks to plaintext buffer. 3828005SMark.Powers@Sun.COM * Ciphertext will be decrypted in the final. 3838005SMark.Powers@Sun.COM */ 3848005SMark.Powers@Sun.COM if (length > 0) { 3858005SMark.Powers@Sun.COM new_len = ctx->gcm_pt_buf_len + length; 3868005SMark.Powers@Sun.COM #ifdef _KERNEL 3878005SMark.Powers@Sun.COM new = kmem_alloc(new_len, ctx->gcm_kmflag); 3888005SMark.Powers@Sun.COM bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 3898005SMark.Powers@Sun.COM kmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len); 3908005SMark.Powers@Sun.COM #else 3918005SMark.Powers@Sun.COM new = malloc(new_len); 3928005SMark.Powers@Sun.COM bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 3938005SMark.Powers@Sun.COM free(ctx->gcm_pt_buf); 3948005SMark.Powers@Sun.COM #endif 3958005SMark.Powers@Sun.COM if (new == NULL) 3968005SMark.Powers@Sun.COM return (CRYPTO_HOST_MEMORY); 3978005SMark.Powers@Sun.COM 3988005SMark.Powers@Sun.COM ctx->gcm_pt_buf = new; 3998005SMark.Powers@Sun.COM ctx->gcm_pt_buf_len = new_len; 4008005SMark.Powers@Sun.COM bcopy(data, &ctx->gcm_pt_buf[ctx->gcm_processed_data_len], 4018005SMark.Powers@Sun.COM length); 4028005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += length; 4038005SMark.Powers@Sun.COM } 4048005SMark.Powers@Sun.COM 4058005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 4068005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 4078005SMark.Powers@Sun.COM } 4088005SMark.Powers@Sun.COM 4098005SMark.Powers@Sun.COM int 4108005SMark.Powers@Sun.COM gcm_decrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 4118005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 4128005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 4138005SMark.Powers@Sun.COM { 4148005SMark.Powers@Sun.COM size_t pt_len; 4158005SMark.Powers@Sun.COM size_t remainder; 4168005SMark.Powers@Sun.COM uint8_t *ghash; 4178005SMark.Powers@Sun.COM uint8_t *blockp; 4188005SMark.Powers@Sun.COM uint8_t *cbp; 4198005SMark.Powers@Sun.COM uint64_t counter; 4208005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 4218005SMark.Powers@Sun.COM int processed = 0, rv; 4228005SMark.Powers@Sun.COM 4238005SMark.Powers@Sun.COM ASSERT(ctx->gcm_processed_data_len == ctx->gcm_pt_buf_len); 4248005SMark.Powers@Sun.COM 4258005SMark.Powers@Sun.COM pt_len = ctx->gcm_processed_data_len - ctx->gcm_tag_len; 4268005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 4278005SMark.Powers@Sun.COM blockp = ctx->gcm_pt_buf; 4288005SMark.Powers@Sun.COM remainder = pt_len; 4298005SMark.Powers@Sun.COM while (remainder > 0) { 4308005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 4318005SMark.Powers@Sun.COM GHASH(ctx, blockp, ghash); 4328005SMark.Powers@Sun.COM 4338005SMark.Powers@Sun.COM /* 4348005SMark.Powers@Sun.COM * Increment counter. 4358005SMark.Powers@Sun.COM * Counter bits are confined to the bottom 32 bits 4368005SMark.Powers@Sun.COM */ 4378005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 4388005SMark.Powers@Sun.COM counter = htonll(counter + 1); 4398005SMark.Powers@Sun.COM counter &= counter_mask; 4408005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 4418005SMark.Powers@Sun.COM 4428005SMark.Powers@Sun.COM cbp = (uint8_t *)ctx->gcm_tmp; 4438005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, cbp); 4448005SMark.Powers@Sun.COM 4458005SMark.Powers@Sun.COM /* XOR with ciphertext */ 4468005SMark.Powers@Sun.COM xor_block(cbp, blockp); 4478005SMark.Powers@Sun.COM 4488005SMark.Powers@Sun.COM processed += block_size; 4498005SMark.Powers@Sun.COM blockp += block_size; 4508005SMark.Powers@Sun.COM remainder -= block_size; 4518005SMark.Powers@Sun.COM 4528005SMark.Powers@Sun.COM /* Incomplete last block */ 4538005SMark.Powers@Sun.COM if (remainder > 0 && remainder < block_size) { 4548005SMark.Powers@Sun.COM bcopy(blockp, ctx->gcm_remainder, remainder); 4558005SMark.Powers@Sun.COM ctx->gcm_remainder_len = remainder; 4568005SMark.Powers@Sun.COM /* 4578005SMark.Powers@Sun.COM * not expecting anymore ciphertext, just 4588005SMark.Powers@Sun.COM * compute plaintext for the remaining input 4598005SMark.Powers@Sun.COM */ 4608005SMark.Powers@Sun.COM gcm_decrypt_incomplete_block(ctx, block_size, 4618005SMark.Powers@Sun.COM processed, encrypt_block, xor_block); 4628005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 4638005SMark.Powers@Sun.COM goto out; 4648005SMark.Powers@Sun.COM } 4658005SMark.Powers@Sun.COM } 4668005SMark.Powers@Sun.COM out: 467*11413Sopensolaris@drydog.com ctx->gcm_len_a_len_c[1] = htonll(CRYPTO_BYTES2BITS(pt_len)); 4688005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 4698005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 4708005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_J0); 4718005SMark.Powers@Sun.COM xor_block((uint8_t *)ctx->gcm_J0, ghash); 4728005SMark.Powers@Sun.COM 4738005SMark.Powers@Sun.COM /* compare the input authentication tag with what we calculated */ 4748005SMark.Powers@Sun.COM if (bcmp(&ctx->gcm_pt_buf[pt_len], ghash, ctx->gcm_tag_len)) { 4758005SMark.Powers@Sun.COM /* They don't match */ 4768005SMark.Powers@Sun.COM return (CRYPTO_INVALID_MAC); 4778005SMark.Powers@Sun.COM } else { 4788005SMark.Powers@Sun.COM rv = crypto_put_output_data(ctx->gcm_pt_buf, out, pt_len); 4798005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 4808005SMark.Powers@Sun.COM return (rv); 4818005SMark.Powers@Sun.COM out->cd_offset += pt_len; 4828005SMark.Powers@Sun.COM } 4838005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 4848005SMark.Powers@Sun.COM } 4858005SMark.Powers@Sun.COM 4868005SMark.Powers@Sun.COM static int 4878005SMark.Powers@Sun.COM gcm_validate_args(CK_AES_GCM_PARAMS *gcm_param) 4888005SMark.Powers@Sun.COM { 4898005SMark.Powers@Sun.COM size_t tag_len; 4908005SMark.Powers@Sun.COM 4918005SMark.Powers@Sun.COM /* 4928005SMark.Powers@Sun.COM * Check the length of the authentication tag (in bits). 4938005SMark.Powers@Sun.COM */ 4948005SMark.Powers@Sun.COM tag_len = gcm_param->ulTagBits; 4958005SMark.Powers@Sun.COM switch (tag_len) { 4968005SMark.Powers@Sun.COM case 32: 4978005SMark.Powers@Sun.COM case 64: 4988005SMark.Powers@Sun.COM case 96: 4998005SMark.Powers@Sun.COM case 104: 5008005SMark.Powers@Sun.COM case 112: 5018005SMark.Powers@Sun.COM case 120: 5028005SMark.Powers@Sun.COM case 128: 5038005SMark.Powers@Sun.COM break; 5048005SMark.Powers@Sun.COM default: 5058005SMark.Powers@Sun.COM return (CRYPTO_MECHANISM_PARAM_INVALID); 5068005SMark.Powers@Sun.COM } 5078005SMark.Powers@Sun.COM 5088005SMark.Powers@Sun.COM if (gcm_param->ulIvLen == 0) 5098005SMark.Powers@Sun.COM return (CRYPTO_MECHANISM_PARAM_INVALID); 5108005SMark.Powers@Sun.COM 5118005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 5128005SMark.Powers@Sun.COM } 5138005SMark.Powers@Sun.COM 5148005SMark.Powers@Sun.COM static void 5158005SMark.Powers@Sun.COM gcm_format_initial_blocks(uchar_t *iv, ulong_t iv_len, 5168005SMark.Powers@Sun.COM gcm_ctx_t *ctx, size_t block_size, 5178005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 5188005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 5198005SMark.Powers@Sun.COM { 5208005SMark.Powers@Sun.COM uint8_t *cb; 5218005SMark.Powers@Sun.COM ulong_t remainder = iv_len; 5228005SMark.Powers@Sun.COM ulong_t processed = 0; 5238005SMark.Powers@Sun.COM uint8_t *datap, *ghash; 5248005SMark.Powers@Sun.COM uint64_t len_a_len_c[2]; 5258005SMark.Powers@Sun.COM 5268005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 5278005SMark.Powers@Sun.COM cb = (uint8_t *)ctx->gcm_cb; 5288005SMark.Powers@Sun.COM if (iv_len == 12) { 5298005SMark.Powers@Sun.COM bcopy(iv, cb, 12); 5308005SMark.Powers@Sun.COM cb[12] = 0; 5318005SMark.Powers@Sun.COM cb[13] = 0; 5328005SMark.Powers@Sun.COM cb[14] = 0; 5338005SMark.Powers@Sun.COM cb[15] = 1; 5348005SMark.Powers@Sun.COM /* J0 will be used again in the final */ 5358005SMark.Powers@Sun.COM copy_block(cb, (uint8_t *)ctx->gcm_J0); 5368005SMark.Powers@Sun.COM } else { 5378005SMark.Powers@Sun.COM /* GHASH the IV */ 5388005SMark.Powers@Sun.COM do { 5398005SMark.Powers@Sun.COM if (remainder < block_size) { 5408005SMark.Powers@Sun.COM bzero(cb, block_size); 5418005SMark.Powers@Sun.COM bcopy(&(iv[processed]), cb, remainder); 5428005SMark.Powers@Sun.COM datap = (uint8_t *)cb; 5438005SMark.Powers@Sun.COM remainder = 0; 5448005SMark.Powers@Sun.COM } else { 5458005SMark.Powers@Sun.COM datap = (uint8_t *)(&(iv[processed])); 5468005SMark.Powers@Sun.COM processed += block_size; 5478005SMark.Powers@Sun.COM remainder -= block_size; 5488005SMark.Powers@Sun.COM } 5498005SMark.Powers@Sun.COM GHASH(ctx, datap, ghash); 5508005SMark.Powers@Sun.COM } while (remainder > 0); 5518005SMark.Powers@Sun.COM 5528005SMark.Powers@Sun.COM len_a_len_c[0] = 0; 553*11413Sopensolaris@drydog.com len_a_len_c[1] = htonll(CRYPTO_BYTES2BITS(iv_len)); 5548005SMark.Powers@Sun.COM GHASH(ctx, len_a_len_c, ctx->gcm_J0); 5558005SMark.Powers@Sun.COM 5568005SMark.Powers@Sun.COM /* J0 will be used again in the final */ 5578005SMark.Powers@Sun.COM copy_block((uint8_t *)ctx->gcm_J0, (uint8_t *)cb); 5588005SMark.Powers@Sun.COM } 5598005SMark.Powers@Sun.COM } 5608005SMark.Powers@Sun.COM 5618005SMark.Powers@Sun.COM /* 5628005SMark.Powers@Sun.COM * The following function is called at encrypt or decrypt init time 5638005SMark.Powers@Sun.COM * for AES GCM mode. 5648005SMark.Powers@Sun.COM */ 5658005SMark.Powers@Sun.COM int 5668005SMark.Powers@Sun.COM gcm_init(gcm_ctx_t *ctx, unsigned char *iv, size_t iv_len, 5678005SMark.Powers@Sun.COM unsigned char *auth_data, size_t auth_data_len, size_t block_size, 5688005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 5698005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 5708005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 5718005SMark.Powers@Sun.COM { 5728005SMark.Powers@Sun.COM uint8_t *ghash, *datap, *authp; 5738005SMark.Powers@Sun.COM size_t remainder, processed; 5748005SMark.Powers@Sun.COM 5758005SMark.Powers@Sun.COM /* encrypt zero block to get subkey H */ 5768005SMark.Powers@Sun.COM bzero(ctx->gcm_H, sizeof (ctx->gcm_H)); 5778005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_H, 5788005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_H); 5798005SMark.Powers@Sun.COM 5808005SMark.Powers@Sun.COM gcm_format_initial_blocks(iv, iv_len, ctx, block_size, 5818005SMark.Powers@Sun.COM copy_block, xor_block); 5828005SMark.Powers@Sun.COM 5838005SMark.Powers@Sun.COM authp = (uint8_t *)ctx->gcm_tmp; 5848005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 5858005SMark.Powers@Sun.COM bzero(authp, block_size); 5868005SMark.Powers@Sun.COM bzero(ghash, block_size); 5878005SMark.Powers@Sun.COM 5888005SMark.Powers@Sun.COM processed = 0; 5898005SMark.Powers@Sun.COM remainder = auth_data_len; 5908005SMark.Powers@Sun.COM do { 5918005SMark.Powers@Sun.COM if (remainder < block_size) { 5928005SMark.Powers@Sun.COM /* 5938005SMark.Powers@Sun.COM * There's not a block full of data, pad rest of 5948005SMark.Powers@Sun.COM * buffer with zero 5958005SMark.Powers@Sun.COM */ 5968005SMark.Powers@Sun.COM bzero(authp, block_size); 5978005SMark.Powers@Sun.COM bcopy(&(auth_data[processed]), authp, remainder); 5988005SMark.Powers@Sun.COM datap = (uint8_t *)authp; 5998005SMark.Powers@Sun.COM remainder = 0; 6008005SMark.Powers@Sun.COM } else { 6018005SMark.Powers@Sun.COM datap = (uint8_t *)(&(auth_data[processed])); 6028005SMark.Powers@Sun.COM processed += block_size; 6038005SMark.Powers@Sun.COM remainder -= block_size; 6048005SMark.Powers@Sun.COM } 6058005SMark.Powers@Sun.COM 6068005SMark.Powers@Sun.COM /* add auth data to the hash */ 6078005SMark.Powers@Sun.COM GHASH(ctx, datap, ghash); 6088005SMark.Powers@Sun.COM 6098005SMark.Powers@Sun.COM } while (remainder > 0); 6108005SMark.Powers@Sun.COM 6118005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 6128005SMark.Powers@Sun.COM } 6138005SMark.Powers@Sun.COM 6148005SMark.Powers@Sun.COM int 6158005SMark.Powers@Sun.COM gcm_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size, 6168005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 6178005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 6188005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 6198005SMark.Powers@Sun.COM { 6208005SMark.Powers@Sun.COM int rv; 6218005SMark.Powers@Sun.COM CK_AES_GCM_PARAMS *gcm_param; 6228005SMark.Powers@Sun.COM 6238005SMark.Powers@Sun.COM if (param != NULL) { 624*11413Sopensolaris@drydog.com gcm_param = (CK_AES_GCM_PARAMS *)(void *)param; 6258005SMark.Powers@Sun.COM 6268005SMark.Powers@Sun.COM if ((rv = gcm_validate_args(gcm_param)) != 0) { 6278005SMark.Powers@Sun.COM return (rv); 6288005SMark.Powers@Sun.COM } 6298005SMark.Powers@Sun.COM 6308005SMark.Powers@Sun.COM gcm_ctx->gcm_tag_len = gcm_param->ulTagBits; 6318005SMark.Powers@Sun.COM gcm_ctx->gcm_tag_len >>= 3; 6328005SMark.Powers@Sun.COM gcm_ctx->gcm_processed_data_len = 0; 6338005SMark.Powers@Sun.COM 6348005SMark.Powers@Sun.COM /* these values are in bits */ 635*11413Sopensolaris@drydog.com gcm_ctx->gcm_len_a_len_c[0] 636*11413Sopensolaris@drydog.com = htonll(CRYPTO_BYTES2BITS(gcm_param->ulAADLen)); 6378005SMark.Powers@Sun.COM 6388005SMark.Powers@Sun.COM rv = CRYPTO_SUCCESS; 6398005SMark.Powers@Sun.COM gcm_ctx->gcm_flags |= GCM_MODE; 6408005SMark.Powers@Sun.COM } else { 6418005SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 6428005SMark.Powers@Sun.COM goto out; 6438005SMark.Powers@Sun.COM } 6448005SMark.Powers@Sun.COM 6458005SMark.Powers@Sun.COM if (gcm_init(gcm_ctx, gcm_param->pIv, gcm_param->ulIvLen, 6468005SMark.Powers@Sun.COM gcm_param->pAAD, gcm_param->ulAADLen, block_size, 6478005SMark.Powers@Sun.COM encrypt_block, copy_block, xor_block) != 0) { 6488005SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 6498005SMark.Powers@Sun.COM } 6508005SMark.Powers@Sun.COM out: 6518005SMark.Powers@Sun.COM return (rv); 6528005SMark.Powers@Sun.COM } 6538005SMark.Powers@Sun.COM 6549339SMark.Powers@Sun.COM int 6559339SMark.Powers@Sun.COM gmac_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size, 6569339SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 6579339SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 6589339SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 6599339SMark.Powers@Sun.COM { 6609339SMark.Powers@Sun.COM int rv; 6619339SMark.Powers@Sun.COM CK_AES_GMAC_PARAMS *gmac_param; 6629339SMark.Powers@Sun.COM 6639339SMark.Powers@Sun.COM if (param != NULL) { 664*11413Sopensolaris@drydog.com gmac_param = (CK_AES_GMAC_PARAMS *)(void *)param; 6659339SMark.Powers@Sun.COM 6669339SMark.Powers@Sun.COM gcm_ctx->gcm_tag_len = CRYPTO_BITS2BYTES(AES_GMAC_TAG_BITS); 6679339SMark.Powers@Sun.COM gcm_ctx->gcm_processed_data_len = 0; 6689339SMark.Powers@Sun.COM 6699339SMark.Powers@Sun.COM /* these values are in bits */ 670*11413Sopensolaris@drydog.com gcm_ctx->gcm_len_a_len_c[0] 671*11413Sopensolaris@drydog.com = htonll(CRYPTO_BYTES2BITS(gmac_param->ulAADLen)); 6729339SMark.Powers@Sun.COM 6739339SMark.Powers@Sun.COM rv = CRYPTO_SUCCESS; 6749339SMark.Powers@Sun.COM gcm_ctx->gcm_flags |= GMAC_MODE; 6759339SMark.Powers@Sun.COM } else { 6769339SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 6779339SMark.Powers@Sun.COM goto out; 6789339SMark.Powers@Sun.COM } 6799339SMark.Powers@Sun.COM 6809339SMark.Powers@Sun.COM if (gcm_init(gcm_ctx, gmac_param->pIv, AES_GMAC_IV_LEN, 6819339SMark.Powers@Sun.COM gmac_param->pAAD, gmac_param->ulAADLen, block_size, 6829339SMark.Powers@Sun.COM encrypt_block, copy_block, xor_block) != 0) { 6839339SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 6849339SMark.Powers@Sun.COM } 6859339SMark.Powers@Sun.COM out: 6869339SMark.Powers@Sun.COM return (rv); 6879339SMark.Powers@Sun.COM } 6889339SMark.Powers@Sun.COM 6898005SMark.Powers@Sun.COM void * 6908005SMark.Powers@Sun.COM gcm_alloc_ctx(int kmflag) 6918005SMark.Powers@Sun.COM { 6928005SMark.Powers@Sun.COM gcm_ctx_t *gcm_ctx; 6938005SMark.Powers@Sun.COM 6948005SMark.Powers@Sun.COM #ifdef _KERNEL 6958005SMark.Powers@Sun.COM if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL) 6968005SMark.Powers@Sun.COM #else 6978005SMark.Powers@Sun.COM if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL) 6988005SMark.Powers@Sun.COM #endif 6998005SMark.Powers@Sun.COM return (NULL); 7008005SMark.Powers@Sun.COM 7018005SMark.Powers@Sun.COM gcm_ctx->gcm_flags = GCM_MODE; 7028005SMark.Powers@Sun.COM return (gcm_ctx); 7038005SMark.Powers@Sun.COM } 7048005SMark.Powers@Sun.COM 7059339SMark.Powers@Sun.COM void * 7069339SMark.Powers@Sun.COM gmac_alloc_ctx(int kmflag) 7079339SMark.Powers@Sun.COM { 7089339SMark.Powers@Sun.COM gcm_ctx_t *gcm_ctx; 7099339SMark.Powers@Sun.COM 7109339SMark.Powers@Sun.COM #ifdef _KERNEL 7119339SMark.Powers@Sun.COM if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL) 7129339SMark.Powers@Sun.COM #else 7139339SMark.Powers@Sun.COM if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL) 7149339SMark.Powers@Sun.COM #endif 7159339SMark.Powers@Sun.COM return (NULL); 7169339SMark.Powers@Sun.COM 7179339SMark.Powers@Sun.COM gcm_ctx->gcm_flags = GMAC_MODE; 7189339SMark.Powers@Sun.COM return (gcm_ctx); 7199339SMark.Powers@Sun.COM } 7209339SMark.Powers@Sun.COM 7218005SMark.Powers@Sun.COM void 7228005SMark.Powers@Sun.COM gcm_set_kmflag(gcm_ctx_t *ctx, int kmflag) 7238005SMark.Powers@Sun.COM { 7248005SMark.Powers@Sun.COM ctx->gcm_kmflag = kmflag; 7258005SMark.Powers@Sun.COM } 72610627Sopensolaris@drydog.com 72710627Sopensolaris@drydog.com 72810627Sopensolaris@drydog.com #ifdef __amd64 72910627Sopensolaris@drydog.com /* 73010627Sopensolaris@drydog.com * Return 1 if executing on Intel with PCLMULQDQ instructions, 73110627Sopensolaris@drydog.com * otherwise 0 (i.e., Intel without PCLMULQDQ or AMD64). 73210627Sopensolaris@drydog.com * Cache the result, as the CPU can't change. 73310627Sopensolaris@drydog.com * 73410627Sopensolaris@drydog.com * Note: the userland version uses getisax(). The kernel version uses 73510627Sopensolaris@drydog.com * global variable x86_feature or the output of cpuid_insn(). 73610627Sopensolaris@drydog.com */ 73710627Sopensolaris@drydog.com static int 73810627Sopensolaris@drydog.com intel_pclmulqdq_instruction_present(void) 73910627Sopensolaris@drydog.com { 74010627Sopensolaris@drydog.com static int cached_result = -1; 74110627Sopensolaris@drydog.com 74210627Sopensolaris@drydog.com if (cached_result == -1) { /* first time */ 74310627Sopensolaris@drydog.com #ifdef _KERNEL 74410627Sopensolaris@drydog.com #ifdef X86_PCLMULQDQ 74510627Sopensolaris@drydog.com cached_result = (x86_feature & X86_PCLMULQDQ) != 0; 74610627Sopensolaris@drydog.com #else 74710627Sopensolaris@drydog.com if (cpuid_getvendor(CPU) == X86_VENDOR_Intel) { 74810627Sopensolaris@drydog.com struct cpuid_regs cpr; 74910627Sopensolaris@drydog.com cpu_t *cp = CPU; 75010627Sopensolaris@drydog.com 75110627Sopensolaris@drydog.com cpr.cp_eax = 1; /* Function 1: get processor info */ 75210627Sopensolaris@drydog.com (void) cpuid_insn(cp, &cpr); 75310627Sopensolaris@drydog.com cached_result = ((cpr.cp_ecx & 75410627Sopensolaris@drydog.com CPUID_INTC_ECX_PCLMULQDQ) != 0); 75510627Sopensolaris@drydog.com } else { 75610627Sopensolaris@drydog.com cached_result = 0; 75710627Sopensolaris@drydog.com } 75810627Sopensolaris@drydog.com #endif /* X86_PCLMULQDQ */ 75910627Sopensolaris@drydog.com #else 76010627Sopensolaris@drydog.com uint_t ui = 0; 76110627Sopensolaris@drydog.com 76210627Sopensolaris@drydog.com (void) getisax(&ui, 1); 76310627Sopensolaris@drydog.com cached_result = (ui & AV_386_PCLMULQDQ) != 0; 76410627Sopensolaris@drydog.com #endif /* _KERNEL */ 76510627Sopensolaris@drydog.com } 76610627Sopensolaris@drydog.com 76710627Sopensolaris@drydog.com return (cached_result); 76810627Sopensolaris@drydog.com } 76910627Sopensolaris@drydog.com #endif /* __amd64 */ 770