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*8559SMark.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 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> 318005SMark.Powers@Sun.COM #endif 328005SMark.Powers@Sun.COM 338005SMark.Powers@Sun.COM #include <sys/types.h> 348005SMark.Powers@Sun.COM #include <sys/kmem.h> 358005SMark.Powers@Sun.COM #include <modes/modes.h> 368005SMark.Powers@Sun.COM #include <sys/crypto/common.h> 378005SMark.Powers@Sun.COM #include <sys/crypto/impl.h> 388005SMark.Powers@Sun.COM #include <sys/byteorder.h> 398005SMark.Powers@Sun.COM 408005SMark.Powers@Sun.COM struct aes_block { 418005SMark.Powers@Sun.COM uint64_t a; 428005SMark.Powers@Sun.COM uint64_t b; 438005SMark.Powers@Sun.COM }; 448005SMark.Powers@Sun.COM 45*8559SMark.Powers@Sun.COM void 468005SMark.Powers@Sun.COM gcm_mul(uint64_t *x_in, uint64_t *y, uint64_t *res) 478005SMark.Powers@Sun.COM { 488005SMark.Powers@Sun.COM uint64_t R = { 0xe100000000000000ULL }; 498005SMark.Powers@Sun.COM struct aes_block z = { 0, 0 }; 508005SMark.Powers@Sun.COM struct aes_block v; 518005SMark.Powers@Sun.COM uint64_t x; 528005SMark.Powers@Sun.COM int i, j; 538005SMark.Powers@Sun.COM 548005SMark.Powers@Sun.COM v.a = ntohll(y[0]); 558005SMark.Powers@Sun.COM v.b = ntohll(y[1]); 568005SMark.Powers@Sun.COM 578005SMark.Powers@Sun.COM for (j = 0; j < 2; j++) { 588005SMark.Powers@Sun.COM x = ntohll(x_in[j]); 598005SMark.Powers@Sun.COM for (i = 0; i < 64; i++, x <<= 1) { 608005SMark.Powers@Sun.COM if (x & 0x8000000000000000ULL) { 618005SMark.Powers@Sun.COM z.a ^= v.a; 628005SMark.Powers@Sun.COM z.b ^= v.b; 638005SMark.Powers@Sun.COM } 648005SMark.Powers@Sun.COM if (v.b & 1ULL) { 658005SMark.Powers@Sun.COM v.b = (v.a << 63)|(v.b >> 1); 668005SMark.Powers@Sun.COM v.a = (v.a >> 1) ^ R; 678005SMark.Powers@Sun.COM } else { 688005SMark.Powers@Sun.COM v.b = (v.a << 63)|(v.b >> 1); 698005SMark.Powers@Sun.COM v.a = v.a >> 1; 708005SMark.Powers@Sun.COM } 718005SMark.Powers@Sun.COM } 728005SMark.Powers@Sun.COM } 738005SMark.Powers@Sun.COM res[0] = htonll(z.a); 748005SMark.Powers@Sun.COM res[1] = htonll(z.b); 758005SMark.Powers@Sun.COM } 768005SMark.Powers@Sun.COM 778005SMark.Powers@Sun.COM #define GHASH(c, d, t) \ 788005SMark.Powers@Sun.COM xor_block((uint8_t *)(d), (uint8_t *)(c)->gcm_ghash); \ 798005SMark.Powers@Sun.COM gcm_mul((uint64_t *)(c)->gcm_ghash, (c)->gcm_H, (uint64_t *)(t)); 808005SMark.Powers@Sun.COM 818005SMark.Powers@Sun.COM /* 828005SMark.Powers@Sun.COM * Encrypt multiple blocks of data in GCM mode. Decrypt for GCM mode 838005SMark.Powers@Sun.COM * is done in another function. 848005SMark.Powers@Sun.COM */ 858005SMark.Powers@Sun.COM int 868005SMark.Powers@Sun.COM gcm_mode_encrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 878005SMark.Powers@Sun.COM crypto_data_t *out, size_t block_size, 888005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 898005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 908005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 918005SMark.Powers@Sun.COM { 928005SMark.Powers@Sun.COM size_t remainder = length; 938005SMark.Powers@Sun.COM size_t need; 948005SMark.Powers@Sun.COM uint8_t *datap = (uint8_t *)data; 958005SMark.Powers@Sun.COM uint8_t *blockp; 968005SMark.Powers@Sun.COM uint8_t *lastp; 978005SMark.Powers@Sun.COM void *iov_or_mp; 988005SMark.Powers@Sun.COM offset_t offset; 998005SMark.Powers@Sun.COM uint8_t *out_data_1; 1008005SMark.Powers@Sun.COM uint8_t *out_data_2; 1018005SMark.Powers@Sun.COM size_t out_data_1_len; 1028005SMark.Powers@Sun.COM uint64_t counter; 1038005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 1048005SMark.Powers@Sun.COM 1058005SMark.Powers@Sun.COM if (length + ctx->gcm_remainder_len < block_size) { 1068005SMark.Powers@Sun.COM /* accumulate bytes here and return */ 1078005SMark.Powers@Sun.COM bcopy(datap, 1088005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_remainder + ctx->gcm_remainder_len, 1098005SMark.Powers@Sun.COM length); 1108005SMark.Powers@Sun.COM ctx->gcm_remainder_len += length; 1118005SMark.Powers@Sun.COM ctx->gcm_copy_to = datap; 1128005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 1138005SMark.Powers@Sun.COM } 1148005SMark.Powers@Sun.COM 1158005SMark.Powers@Sun.COM lastp = (uint8_t *)ctx->gcm_cb; 1168005SMark.Powers@Sun.COM if (out != NULL) 1178005SMark.Powers@Sun.COM crypto_init_ptrs(out, &iov_or_mp, &offset); 1188005SMark.Powers@Sun.COM 1198005SMark.Powers@Sun.COM do { 1208005SMark.Powers@Sun.COM /* Unprocessed data from last call. */ 1218005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 1228005SMark.Powers@Sun.COM need = block_size - ctx->gcm_remainder_len; 1238005SMark.Powers@Sun.COM 1248005SMark.Powers@Sun.COM if (need > remainder) 1258005SMark.Powers@Sun.COM return (CRYPTO_DATA_LEN_RANGE); 1268005SMark.Powers@Sun.COM 1278005SMark.Powers@Sun.COM bcopy(datap, &((uint8_t *)ctx->gcm_remainder) 1288005SMark.Powers@Sun.COM [ctx->gcm_remainder_len], need); 1298005SMark.Powers@Sun.COM 1308005SMark.Powers@Sun.COM blockp = (uint8_t *)ctx->gcm_remainder; 1318005SMark.Powers@Sun.COM } else { 1328005SMark.Powers@Sun.COM blockp = datap; 1338005SMark.Powers@Sun.COM } 1348005SMark.Powers@Sun.COM 1358005SMark.Powers@Sun.COM /* 1368005SMark.Powers@Sun.COM * Increment counter. Counter bits are confined 1378005SMark.Powers@Sun.COM * to the bottom 32 bits of the counter block. 1388005SMark.Powers@Sun.COM */ 1398005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 1408005SMark.Powers@Sun.COM counter = htonll(counter + 1); 1418005SMark.Powers@Sun.COM counter &= counter_mask; 1428005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 1438005SMark.Powers@Sun.COM 1448005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 1458005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_tmp); 1468005SMark.Powers@Sun.COM xor_block(blockp, (uint8_t *)ctx->gcm_tmp); 1478005SMark.Powers@Sun.COM 1488005SMark.Powers@Sun.COM lastp = (uint8_t *)ctx->gcm_tmp; 1498005SMark.Powers@Sun.COM 1508005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += block_size; 1518005SMark.Powers@Sun.COM 1528005SMark.Powers@Sun.COM if (out == NULL) { 1538005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 1548005SMark.Powers@Sun.COM bcopy(blockp, ctx->gcm_copy_to, 1558005SMark.Powers@Sun.COM ctx->gcm_remainder_len); 1568005SMark.Powers@Sun.COM bcopy(blockp + ctx->gcm_remainder_len, datap, 1578005SMark.Powers@Sun.COM need); 1588005SMark.Powers@Sun.COM } 1598005SMark.Powers@Sun.COM } else { 1608005SMark.Powers@Sun.COM crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 1618005SMark.Powers@Sun.COM &out_data_1_len, &out_data_2, block_size); 1628005SMark.Powers@Sun.COM 1638005SMark.Powers@Sun.COM /* copy block to where it belongs */ 1648005SMark.Powers@Sun.COM if (out_data_1_len == block_size) { 1658005SMark.Powers@Sun.COM copy_block(lastp, out_data_1); 1668005SMark.Powers@Sun.COM } else { 1678005SMark.Powers@Sun.COM bcopy(lastp, out_data_1, out_data_1_len); 1688005SMark.Powers@Sun.COM if (out_data_2 != NULL) { 1698005SMark.Powers@Sun.COM bcopy(lastp + out_data_1_len, 1708005SMark.Powers@Sun.COM out_data_2, 1718005SMark.Powers@Sun.COM block_size - out_data_1_len); 1728005SMark.Powers@Sun.COM } 1738005SMark.Powers@Sun.COM } 1748005SMark.Powers@Sun.COM /* update offset */ 1758005SMark.Powers@Sun.COM out->cd_offset += block_size; 1768005SMark.Powers@Sun.COM } 1778005SMark.Powers@Sun.COM 1788005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 1798005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 1808005SMark.Powers@Sun.COM 1818005SMark.Powers@Sun.COM /* Update pointer to next block of data to be processed. */ 1828005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len != 0) { 1838005SMark.Powers@Sun.COM datap += need; 1848005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 1858005SMark.Powers@Sun.COM } else { 1868005SMark.Powers@Sun.COM datap += block_size; 1878005SMark.Powers@Sun.COM } 1888005SMark.Powers@Sun.COM 1898005SMark.Powers@Sun.COM remainder = (size_t)&data[length] - (size_t)datap; 1908005SMark.Powers@Sun.COM 1918005SMark.Powers@Sun.COM /* Incomplete last block. */ 1928005SMark.Powers@Sun.COM if (remainder > 0 && remainder < block_size) { 1938005SMark.Powers@Sun.COM bcopy(datap, ctx->gcm_remainder, remainder); 1948005SMark.Powers@Sun.COM ctx->gcm_remainder_len = remainder; 1958005SMark.Powers@Sun.COM ctx->gcm_copy_to = datap; 1968005SMark.Powers@Sun.COM goto out; 1978005SMark.Powers@Sun.COM } 1988005SMark.Powers@Sun.COM ctx->gcm_copy_to = NULL; 1998005SMark.Powers@Sun.COM 2008005SMark.Powers@Sun.COM } while (remainder > 0); 2018005SMark.Powers@Sun.COM out: 2028005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 2038005SMark.Powers@Sun.COM } 2048005SMark.Powers@Sun.COM 2058005SMark.Powers@Sun.COM /* ARGSUSED */ 2068005SMark.Powers@Sun.COM int 2078005SMark.Powers@Sun.COM gcm_encrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 2088005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 2098005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 2108005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 2118005SMark.Powers@Sun.COM { 2128005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 2138005SMark.Powers@Sun.COM uint8_t *ghash, *macp; 2148005SMark.Powers@Sun.COM int i, rv; 2158005SMark.Powers@Sun.COM 2168005SMark.Powers@Sun.COM if (out->cd_length < 2178005SMark.Powers@Sun.COM (ctx->gcm_remainder_len + ctx->gcm_tag_len)) { 2188005SMark.Powers@Sun.COM return (CRYPTO_DATA_LEN_RANGE); 2198005SMark.Powers@Sun.COM } 2208005SMark.Powers@Sun.COM 2218005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 2228005SMark.Powers@Sun.COM 2238005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 2248005SMark.Powers@Sun.COM uint64_t counter; 2258005SMark.Powers@Sun.COM uint8_t *tmpp = (uint8_t *)ctx->gcm_tmp; 2268005SMark.Powers@Sun.COM 2278005SMark.Powers@Sun.COM /* 2288005SMark.Powers@Sun.COM * Here is where we deal with data that is not a 2298005SMark.Powers@Sun.COM * multiple of the block size. 2308005SMark.Powers@Sun.COM */ 2318005SMark.Powers@Sun.COM 2328005SMark.Powers@Sun.COM /* 2338005SMark.Powers@Sun.COM * Increment counter. 2348005SMark.Powers@Sun.COM */ 2358005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 2368005SMark.Powers@Sun.COM counter = htonll(counter + 1); 2378005SMark.Powers@Sun.COM counter &= counter_mask; 2388005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 2398005SMark.Powers@Sun.COM 2408005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 2418005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_tmp); 2428005SMark.Powers@Sun.COM 2438005SMark.Powers@Sun.COM macp = (uint8_t *)ctx->gcm_remainder; 2448005SMark.Powers@Sun.COM bzero(macp + ctx->gcm_remainder_len, 2458005SMark.Powers@Sun.COM block_size - ctx->gcm_remainder_len); 2468005SMark.Powers@Sun.COM 2478005SMark.Powers@Sun.COM /* XOR with counter block */ 2488005SMark.Powers@Sun.COM for (i = 0; i < ctx->gcm_remainder_len; i++) { 2498005SMark.Powers@Sun.COM macp[i] ^= tmpp[i]; 2508005SMark.Powers@Sun.COM } 2518005SMark.Powers@Sun.COM 2528005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 2538005SMark.Powers@Sun.COM GHASH(ctx, macp, ghash); 2548005SMark.Powers@Sun.COM 2558005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += ctx->gcm_remainder_len; 2568005SMark.Powers@Sun.COM } 2578005SMark.Powers@Sun.COM 2588005SMark.Powers@Sun.COM ctx->gcm_len_a_len_c[1] = htonll(ctx->gcm_processed_data_len << 3); 2598005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 2608005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 2618005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_J0); 2628005SMark.Powers@Sun.COM xor_block((uint8_t *)ctx->gcm_J0, ghash); 2638005SMark.Powers@Sun.COM 2648005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 2658005SMark.Powers@Sun.COM rv = crypto_put_output_data(macp, out, ctx->gcm_remainder_len); 2668005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 2678005SMark.Powers@Sun.COM return (rv); 2688005SMark.Powers@Sun.COM } 2698005SMark.Powers@Sun.COM out->cd_offset += ctx->gcm_remainder_len; 2708005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 2718005SMark.Powers@Sun.COM rv = crypto_put_output_data(ghash, out, ctx->gcm_tag_len); 2728005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 2738005SMark.Powers@Sun.COM return (rv); 2748005SMark.Powers@Sun.COM out->cd_offset += ctx->gcm_tag_len; 2758005SMark.Powers@Sun.COM 2768005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 2778005SMark.Powers@Sun.COM } 2788005SMark.Powers@Sun.COM 2798005SMark.Powers@Sun.COM /* 2808005SMark.Powers@Sun.COM * This will only deal with decrypting the last block of the input that 2818005SMark.Powers@Sun.COM * might not be a multiple of block length. 2828005SMark.Powers@Sun.COM */ 2838005SMark.Powers@Sun.COM static void 2848005SMark.Powers@Sun.COM gcm_decrypt_incomplete_block(gcm_ctx_t *ctx, size_t block_size, size_t index, 2858005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 2868005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 2878005SMark.Powers@Sun.COM { 2888005SMark.Powers@Sun.COM uint8_t *datap, *outp, *counterp; 2898005SMark.Powers@Sun.COM uint64_t counter; 2908005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 2918005SMark.Powers@Sun.COM int i; 2928005SMark.Powers@Sun.COM 2938005SMark.Powers@Sun.COM /* 2948005SMark.Powers@Sun.COM * Increment counter. 2958005SMark.Powers@Sun.COM * Counter bits are confined to the bottom 32 bits 2968005SMark.Powers@Sun.COM */ 2978005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 2988005SMark.Powers@Sun.COM counter = htonll(counter + 1); 2998005SMark.Powers@Sun.COM counter &= counter_mask; 3008005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 3018005SMark.Powers@Sun.COM 3028005SMark.Powers@Sun.COM datap = (uint8_t *)ctx->gcm_remainder; 3038005SMark.Powers@Sun.COM outp = &((ctx->gcm_pt_buf)[index]); 3048005SMark.Powers@Sun.COM counterp = (uint8_t *)ctx->gcm_tmp; 3058005SMark.Powers@Sun.COM 3068005SMark.Powers@Sun.COM /* authentication tag */ 3078005SMark.Powers@Sun.COM bzero((uint8_t *)ctx->gcm_tmp, block_size); 3088005SMark.Powers@Sun.COM bcopy(datap, (uint8_t *)ctx->gcm_tmp, ctx->gcm_remainder_len); 3098005SMark.Powers@Sun.COM 3108005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 3118005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 3128005SMark.Powers@Sun.COM 3138005SMark.Powers@Sun.COM /* decrypt remaining ciphertext */ 3148005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, counterp); 3158005SMark.Powers@Sun.COM 3168005SMark.Powers@Sun.COM /* XOR with counter block */ 3178005SMark.Powers@Sun.COM for (i = 0; i < ctx->gcm_remainder_len; i++) { 3188005SMark.Powers@Sun.COM outp[i] = datap[i] ^ counterp[i]; 3198005SMark.Powers@Sun.COM } 3208005SMark.Powers@Sun.COM } 3218005SMark.Powers@Sun.COM 3228005SMark.Powers@Sun.COM /* ARGSUSED */ 3238005SMark.Powers@Sun.COM int 3248005SMark.Powers@Sun.COM gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 3258005SMark.Powers@Sun.COM crypto_data_t *out, size_t block_size, 3268005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 3278005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 3288005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 3298005SMark.Powers@Sun.COM { 3308005SMark.Powers@Sun.COM size_t new_len; 3318005SMark.Powers@Sun.COM uint8_t *new; 3328005SMark.Powers@Sun.COM 3338005SMark.Powers@Sun.COM /* 3348005SMark.Powers@Sun.COM * Copy contiguous ciphertext input blocks to plaintext buffer. 3358005SMark.Powers@Sun.COM * Ciphertext will be decrypted in the final. 3368005SMark.Powers@Sun.COM */ 3378005SMark.Powers@Sun.COM if (length > 0) { 3388005SMark.Powers@Sun.COM new_len = ctx->gcm_pt_buf_len + length; 3398005SMark.Powers@Sun.COM #ifdef _KERNEL 3408005SMark.Powers@Sun.COM new = kmem_alloc(new_len, ctx->gcm_kmflag); 3418005SMark.Powers@Sun.COM bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 3428005SMark.Powers@Sun.COM kmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len); 3438005SMark.Powers@Sun.COM #else 3448005SMark.Powers@Sun.COM new = malloc(new_len); 3458005SMark.Powers@Sun.COM bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 3468005SMark.Powers@Sun.COM free(ctx->gcm_pt_buf); 3478005SMark.Powers@Sun.COM #endif 3488005SMark.Powers@Sun.COM if (new == NULL) 3498005SMark.Powers@Sun.COM return (CRYPTO_HOST_MEMORY); 3508005SMark.Powers@Sun.COM 3518005SMark.Powers@Sun.COM ctx->gcm_pt_buf = new; 3528005SMark.Powers@Sun.COM ctx->gcm_pt_buf_len = new_len; 3538005SMark.Powers@Sun.COM bcopy(data, &ctx->gcm_pt_buf[ctx->gcm_processed_data_len], 3548005SMark.Powers@Sun.COM length); 3558005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += length; 3568005SMark.Powers@Sun.COM } 3578005SMark.Powers@Sun.COM 3588005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 3598005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 3608005SMark.Powers@Sun.COM } 3618005SMark.Powers@Sun.COM 3628005SMark.Powers@Sun.COM int 3638005SMark.Powers@Sun.COM gcm_decrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 3648005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 3658005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 3668005SMark.Powers@Sun.COM { 3678005SMark.Powers@Sun.COM size_t pt_len; 3688005SMark.Powers@Sun.COM size_t remainder; 3698005SMark.Powers@Sun.COM uint8_t *ghash; 3708005SMark.Powers@Sun.COM uint8_t *blockp; 3718005SMark.Powers@Sun.COM uint8_t *cbp; 3728005SMark.Powers@Sun.COM uint64_t counter; 3738005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 3748005SMark.Powers@Sun.COM int processed = 0, rv; 3758005SMark.Powers@Sun.COM 3768005SMark.Powers@Sun.COM ASSERT(ctx->gcm_processed_data_len == ctx->gcm_pt_buf_len); 3778005SMark.Powers@Sun.COM 3788005SMark.Powers@Sun.COM pt_len = ctx->gcm_processed_data_len - ctx->gcm_tag_len; 3798005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 3808005SMark.Powers@Sun.COM blockp = ctx->gcm_pt_buf; 3818005SMark.Powers@Sun.COM remainder = pt_len; 3828005SMark.Powers@Sun.COM while (remainder > 0) { 3838005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 3848005SMark.Powers@Sun.COM GHASH(ctx, blockp, ghash); 3858005SMark.Powers@Sun.COM 3868005SMark.Powers@Sun.COM /* 3878005SMark.Powers@Sun.COM * Increment counter. 3888005SMark.Powers@Sun.COM * Counter bits are confined to the bottom 32 bits 3898005SMark.Powers@Sun.COM */ 3908005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 3918005SMark.Powers@Sun.COM counter = htonll(counter + 1); 3928005SMark.Powers@Sun.COM counter &= counter_mask; 3938005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 3948005SMark.Powers@Sun.COM 3958005SMark.Powers@Sun.COM cbp = (uint8_t *)ctx->gcm_tmp; 3968005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, cbp); 3978005SMark.Powers@Sun.COM 3988005SMark.Powers@Sun.COM /* XOR with ciphertext */ 3998005SMark.Powers@Sun.COM xor_block(cbp, blockp); 4008005SMark.Powers@Sun.COM 4018005SMark.Powers@Sun.COM processed += block_size; 4028005SMark.Powers@Sun.COM blockp += block_size; 4038005SMark.Powers@Sun.COM remainder -= block_size; 4048005SMark.Powers@Sun.COM 4058005SMark.Powers@Sun.COM /* Incomplete last block */ 4068005SMark.Powers@Sun.COM if (remainder > 0 && remainder < block_size) { 4078005SMark.Powers@Sun.COM bcopy(blockp, ctx->gcm_remainder, remainder); 4088005SMark.Powers@Sun.COM ctx->gcm_remainder_len = remainder; 4098005SMark.Powers@Sun.COM /* 4108005SMark.Powers@Sun.COM * not expecting anymore ciphertext, just 4118005SMark.Powers@Sun.COM * compute plaintext for the remaining input 4128005SMark.Powers@Sun.COM */ 4138005SMark.Powers@Sun.COM gcm_decrypt_incomplete_block(ctx, block_size, 4148005SMark.Powers@Sun.COM processed, encrypt_block, xor_block); 4158005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 4168005SMark.Powers@Sun.COM goto out; 4178005SMark.Powers@Sun.COM } 4188005SMark.Powers@Sun.COM } 4198005SMark.Powers@Sun.COM out: 4208005SMark.Powers@Sun.COM ctx->gcm_len_a_len_c[1] = htonll(pt_len << 3); 4218005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 4228005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 4238005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_J0); 4248005SMark.Powers@Sun.COM xor_block((uint8_t *)ctx->gcm_J0, ghash); 4258005SMark.Powers@Sun.COM 4268005SMark.Powers@Sun.COM /* compare the input authentication tag with what we calculated */ 4278005SMark.Powers@Sun.COM if (bcmp(&ctx->gcm_pt_buf[pt_len], ghash, ctx->gcm_tag_len)) { 4288005SMark.Powers@Sun.COM /* They don't match */ 4298005SMark.Powers@Sun.COM return (CRYPTO_INVALID_MAC); 4308005SMark.Powers@Sun.COM } else { 4318005SMark.Powers@Sun.COM rv = crypto_put_output_data(ctx->gcm_pt_buf, out, pt_len); 4328005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 4338005SMark.Powers@Sun.COM return (rv); 4348005SMark.Powers@Sun.COM out->cd_offset += pt_len; 4358005SMark.Powers@Sun.COM } 4368005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 4378005SMark.Powers@Sun.COM } 4388005SMark.Powers@Sun.COM 4398005SMark.Powers@Sun.COM static int 4408005SMark.Powers@Sun.COM gcm_validate_args(CK_AES_GCM_PARAMS *gcm_param) 4418005SMark.Powers@Sun.COM { 4428005SMark.Powers@Sun.COM size_t tag_len; 4438005SMark.Powers@Sun.COM 4448005SMark.Powers@Sun.COM /* 4458005SMark.Powers@Sun.COM * Check the length of the authentication tag (in bits). 4468005SMark.Powers@Sun.COM */ 4478005SMark.Powers@Sun.COM tag_len = gcm_param->ulTagBits; 4488005SMark.Powers@Sun.COM switch (tag_len) { 4498005SMark.Powers@Sun.COM case 32: 4508005SMark.Powers@Sun.COM case 64: 4518005SMark.Powers@Sun.COM case 96: 4528005SMark.Powers@Sun.COM case 104: 4538005SMark.Powers@Sun.COM case 112: 4548005SMark.Powers@Sun.COM case 120: 4558005SMark.Powers@Sun.COM case 128: 4568005SMark.Powers@Sun.COM break; 4578005SMark.Powers@Sun.COM default: 4588005SMark.Powers@Sun.COM return (CRYPTO_MECHANISM_PARAM_INVALID); 4598005SMark.Powers@Sun.COM } 4608005SMark.Powers@Sun.COM 4618005SMark.Powers@Sun.COM if (gcm_param->ulIvLen == 0) 4628005SMark.Powers@Sun.COM return (CRYPTO_MECHANISM_PARAM_INVALID); 4638005SMark.Powers@Sun.COM 4648005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 4658005SMark.Powers@Sun.COM } 4668005SMark.Powers@Sun.COM 4678005SMark.Powers@Sun.COM static void 4688005SMark.Powers@Sun.COM gcm_format_initial_blocks(uchar_t *iv, ulong_t iv_len, 4698005SMark.Powers@Sun.COM gcm_ctx_t *ctx, size_t block_size, 4708005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 4718005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 4728005SMark.Powers@Sun.COM { 4738005SMark.Powers@Sun.COM uint8_t *cb; 4748005SMark.Powers@Sun.COM ulong_t remainder = iv_len; 4758005SMark.Powers@Sun.COM ulong_t processed = 0; 4768005SMark.Powers@Sun.COM uint8_t *datap, *ghash; 4778005SMark.Powers@Sun.COM uint64_t len_a_len_c[2]; 4788005SMark.Powers@Sun.COM 4798005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 4808005SMark.Powers@Sun.COM cb = (uint8_t *)ctx->gcm_cb; 4818005SMark.Powers@Sun.COM if (iv_len == 12) { 4828005SMark.Powers@Sun.COM bcopy(iv, cb, 12); 4838005SMark.Powers@Sun.COM cb[12] = 0; 4848005SMark.Powers@Sun.COM cb[13] = 0; 4858005SMark.Powers@Sun.COM cb[14] = 0; 4868005SMark.Powers@Sun.COM cb[15] = 1; 4878005SMark.Powers@Sun.COM /* J0 will be used again in the final */ 4888005SMark.Powers@Sun.COM copy_block(cb, (uint8_t *)ctx->gcm_J0); 4898005SMark.Powers@Sun.COM } else { 4908005SMark.Powers@Sun.COM /* GHASH the IV */ 4918005SMark.Powers@Sun.COM do { 4928005SMark.Powers@Sun.COM if (remainder < block_size) { 4938005SMark.Powers@Sun.COM bzero(cb, block_size); 4948005SMark.Powers@Sun.COM bcopy(&(iv[processed]), cb, remainder); 4958005SMark.Powers@Sun.COM datap = (uint8_t *)cb; 4968005SMark.Powers@Sun.COM remainder = 0; 4978005SMark.Powers@Sun.COM } else { 4988005SMark.Powers@Sun.COM datap = (uint8_t *)(&(iv[processed])); 4998005SMark.Powers@Sun.COM processed += block_size; 5008005SMark.Powers@Sun.COM remainder -= block_size; 5018005SMark.Powers@Sun.COM } 5028005SMark.Powers@Sun.COM GHASH(ctx, datap, ghash); 5038005SMark.Powers@Sun.COM } while (remainder > 0); 5048005SMark.Powers@Sun.COM 5058005SMark.Powers@Sun.COM len_a_len_c[0] = 0; 5068005SMark.Powers@Sun.COM len_a_len_c[1] = htonll(iv_len << 3); 5078005SMark.Powers@Sun.COM GHASH(ctx, len_a_len_c, ctx->gcm_J0); 5088005SMark.Powers@Sun.COM 5098005SMark.Powers@Sun.COM /* J0 will be used again in the final */ 5108005SMark.Powers@Sun.COM copy_block((uint8_t *)ctx->gcm_J0, (uint8_t *)cb); 5118005SMark.Powers@Sun.COM } 5128005SMark.Powers@Sun.COM } 5138005SMark.Powers@Sun.COM 5148005SMark.Powers@Sun.COM /* 5158005SMark.Powers@Sun.COM * The following function is called at encrypt or decrypt init time 5168005SMark.Powers@Sun.COM * for AES GCM mode. 5178005SMark.Powers@Sun.COM */ 5188005SMark.Powers@Sun.COM int 5198005SMark.Powers@Sun.COM gcm_init(gcm_ctx_t *ctx, unsigned char *iv, size_t iv_len, 5208005SMark.Powers@Sun.COM unsigned char *auth_data, size_t auth_data_len, size_t block_size, 5218005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 5228005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 5238005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 5248005SMark.Powers@Sun.COM { 5258005SMark.Powers@Sun.COM uint8_t *ghash, *datap, *authp; 5268005SMark.Powers@Sun.COM size_t remainder, processed; 5278005SMark.Powers@Sun.COM 5288005SMark.Powers@Sun.COM /* encrypt zero block to get subkey H */ 5298005SMark.Powers@Sun.COM bzero(ctx->gcm_H, sizeof (ctx->gcm_H)); 5308005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_H, 5318005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_H); 5328005SMark.Powers@Sun.COM 5338005SMark.Powers@Sun.COM gcm_format_initial_blocks(iv, iv_len, ctx, block_size, 5348005SMark.Powers@Sun.COM copy_block, xor_block); 5358005SMark.Powers@Sun.COM 5368005SMark.Powers@Sun.COM authp = (uint8_t *)ctx->gcm_tmp; 5378005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 5388005SMark.Powers@Sun.COM bzero(authp, block_size); 5398005SMark.Powers@Sun.COM bzero(ghash, block_size); 5408005SMark.Powers@Sun.COM 5418005SMark.Powers@Sun.COM processed = 0; 5428005SMark.Powers@Sun.COM remainder = auth_data_len; 5438005SMark.Powers@Sun.COM do { 5448005SMark.Powers@Sun.COM if (remainder < block_size) { 5458005SMark.Powers@Sun.COM /* 5468005SMark.Powers@Sun.COM * There's not a block full of data, pad rest of 5478005SMark.Powers@Sun.COM * buffer with zero 5488005SMark.Powers@Sun.COM */ 5498005SMark.Powers@Sun.COM bzero(authp, block_size); 5508005SMark.Powers@Sun.COM bcopy(&(auth_data[processed]), authp, remainder); 5518005SMark.Powers@Sun.COM datap = (uint8_t *)authp; 5528005SMark.Powers@Sun.COM remainder = 0; 5538005SMark.Powers@Sun.COM } else { 5548005SMark.Powers@Sun.COM datap = (uint8_t *)(&(auth_data[processed])); 5558005SMark.Powers@Sun.COM processed += block_size; 5568005SMark.Powers@Sun.COM remainder -= block_size; 5578005SMark.Powers@Sun.COM } 5588005SMark.Powers@Sun.COM 5598005SMark.Powers@Sun.COM /* add auth data to the hash */ 5608005SMark.Powers@Sun.COM GHASH(ctx, datap, ghash); 5618005SMark.Powers@Sun.COM 5628005SMark.Powers@Sun.COM } while (remainder > 0); 5638005SMark.Powers@Sun.COM 5648005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 5658005SMark.Powers@Sun.COM } 5668005SMark.Powers@Sun.COM 5678005SMark.Powers@Sun.COM int 5688005SMark.Powers@Sun.COM gcm_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size, 5698005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 5708005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 5718005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 5728005SMark.Powers@Sun.COM { 5738005SMark.Powers@Sun.COM int rv; 5748005SMark.Powers@Sun.COM CK_AES_GCM_PARAMS *gcm_param; 5758005SMark.Powers@Sun.COM 5768005SMark.Powers@Sun.COM if (param != NULL) { 5778005SMark.Powers@Sun.COM gcm_param = (CK_AES_GCM_PARAMS *)param; 5788005SMark.Powers@Sun.COM 5798005SMark.Powers@Sun.COM if ((rv = gcm_validate_args(gcm_param)) != 0) { 5808005SMark.Powers@Sun.COM return (rv); 5818005SMark.Powers@Sun.COM } 5828005SMark.Powers@Sun.COM 5838005SMark.Powers@Sun.COM gcm_ctx->gcm_tag_len = gcm_param->ulTagBits; 5848005SMark.Powers@Sun.COM gcm_ctx->gcm_tag_len >>= 3; 5858005SMark.Powers@Sun.COM gcm_ctx->gcm_processed_data_len = 0; 5868005SMark.Powers@Sun.COM 5878005SMark.Powers@Sun.COM /* these values are in bits */ 5888005SMark.Powers@Sun.COM gcm_ctx->gcm_len_a_len_c[0] = htonll(gcm_param->ulAADLen << 3); 5898005SMark.Powers@Sun.COM 5908005SMark.Powers@Sun.COM rv = CRYPTO_SUCCESS; 5918005SMark.Powers@Sun.COM gcm_ctx->gcm_flags |= GCM_MODE; 5928005SMark.Powers@Sun.COM } else { 5938005SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 5948005SMark.Powers@Sun.COM goto out; 5958005SMark.Powers@Sun.COM } 5968005SMark.Powers@Sun.COM 5978005SMark.Powers@Sun.COM if (gcm_init(gcm_ctx, gcm_param->pIv, gcm_param->ulIvLen, 5988005SMark.Powers@Sun.COM gcm_param->pAAD, gcm_param->ulAADLen, block_size, 5998005SMark.Powers@Sun.COM encrypt_block, copy_block, xor_block) != 0) { 6008005SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 6018005SMark.Powers@Sun.COM } 6028005SMark.Powers@Sun.COM out: 6038005SMark.Powers@Sun.COM return (rv); 6048005SMark.Powers@Sun.COM } 6058005SMark.Powers@Sun.COM 6068005SMark.Powers@Sun.COM void * 6078005SMark.Powers@Sun.COM gcm_alloc_ctx(int kmflag) 6088005SMark.Powers@Sun.COM { 6098005SMark.Powers@Sun.COM gcm_ctx_t *gcm_ctx; 6108005SMark.Powers@Sun.COM 6118005SMark.Powers@Sun.COM #ifdef _KERNEL 6128005SMark.Powers@Sun.COM if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL) 6138005SMark.Powers@Sun.COM #else 6148005SMark.Powers@Sun.COM if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL) 6158005SMark.Powers@Sun.COM #endif 6168005SMark.Powers@Sun.COM return (NULL); 6178005SMark.Powers@Sun.COM 6188005SMark.Powers@Sun.COM gcm_ctx->gcm_flags = GCM_MODE; 6198005SMark.Powers@Sun.COM return (gcm_ctx); 6208005SMark.Powers@Sun.COM } 6218005SMark.Powers@Sun.COM 6228005SMark.Powers@Sun.COM void 6238005SMark.Powers@Sun.COM gcm_set_kmflag(gcm_ctx_t *ctx, int kmflag) 6248005SMark.Powers@Sun.COM { 6258005SMark.Powers@Sun.COM ctx->gcm_kmflag = kmflag; 6268005SMark.Powers@Sun.COM } 627