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