1*8005SMark.Powers@Sun.COM /* 2*8005SMark.Powers@Sun.COM * CDDL HEADER START 3*8005SMark.Powers@Sun.COM * 4*8005SMark.Powers@Sun.COM * The contents of this file are subject to the terms of the 5*8005SMark.Powers@Sun.COM * Common Development and Distribution License (the "License"). 6*8005SMark.Powers@Sun.COM * You may not use this file except in compliance with the License. 7*8005SMark.Powers@Sun.COM * 8*8005SMark.Powers@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*8005SMark.Powers@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*8005SMark.Powers@Sun.COM * See the License for the specific language governing permissions 11*8005SMark.Powers@Sun.COM * and limitations under the License. 12*8005SMark.Powers@Sun.COM * 13*8005SMark.Powers@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*8005SMark.Powers@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*8005SMark.Powers@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*8005SMark.Powers@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*8005SMark.Powers@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*8005SMark.Powers@Sun.COM * 19*8005SMark.Powers@Sun.COM * CDDL HEADER END 20*8005SMark.Powers@Sun.COM */ 21*8005SMark.Powers@Sun.COM /* 22*8005SMark.Powers@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23*8005SMark.Powers@Sun.COM * Use is subject to license terms. 24*8005SMark.Powers@Sun.COM */ 25*8005SMark.Powers@Sun.COM 26*8005SMark.Powers@Sun.COM #ifndef _KERNEL 27*8005SMark.Powers@Sun.COM #include <strings.h> 28*8005SMark.Powers@Sun.COM #include <limits.h> 29*8005SMark.Powers@Sun.COM #include <assert.h> 30*8005SMark.Powers@Sun.COM #include <security/cryptoki.h> 31*8005SMark.Powers@Sun.COM #endif 32*8005SMark.Powers@Sun.COM 33*8005SMark.Powers@Sun.COM #include <sys/types.h> 34*8005SMark.Powers@Sun.COM #include <sys/kmem.h> 35*8005SMark.Powers@Sun.COM #include <modes/modes.h> 36*8005SMark.Powers@Sun.COM #include <sys/crypto/common.h> 37*8005SMark.Powers@Sun.COM #include <sys/crypto/impl.h> 38*8005SMark.Powers@Sun.COM #include <sys/byteorder.h> 39*8005SMark.Powers@Sun.COM 40*8005SMark.Powers@Sun.COM struct aes_block { 41*8005SMark.Powers@Sun.COM uint64_t a; 42*8005SMark.Powers@Sun.COM uint64_t b; 43*8005SMark.Powers@Sun.COM }; 44*8005SMark.Powers@Sun.COM 45*8005SMark.Powers@Sun.COM static void 46*8005SMark.Powers@Sun.COM gcm_mul(uint64_t *x_in, uint64_t *y, uint64_t *res) 47*8005SMark.Powers@Sun.COM { 48*8005SMark.Powers@Sun.COM uint64_t R = { 0xe100000000000000ULL }; 49*8005SMark.Powers@Sun.COM struct aes_block z = { 0, 0 }; 50*8005SMark.Powers@Sun.COM struct aes_block v; 51*8005SMark.Powers@Sun.COM uint64_t x; 52*8005SMark.Powers@Sun.COM int i, j; 53*8005SMark.Powers@Sun.COM 54*8005SMark.Powers@Sun.COM v.a = ntohll(y[0]); 55*8005SMark.Powers@Sun.COM v.b = ntohll(y[1]); 56*8005SMark.Powers@Sun.COM 57*8005SMark.Powers@Sun.COM for (j = 0; j < 2; j++) { 58*8005SMark.Powers@Sun.COM x = ntohll(x_in[j]); 59*8005SMark.Powers@Sun.COM for (i = 0; i < 64; i++, x <<= 1) { 60*8005SMark.Powers@Sun.COM if (x & 0x8000000000000000ULL) { 61*8005SMark.Powers@Sun.COM z.a ^= v.a; 62*8005SMark.Powers@Sun.COM z.b ^= v.b; 63*8005SMark.Powers@Sun.COM } 64*8005SMark.Powers@Sun.COM if (v.b & 1ULL) { 65*8005SMark.Powers@Sun.COM v.b = (v.a << 63)|(v.b >> 1); 66*8005SMark.Powers@Sun.COM v.a = (v.a >> 1) ^ R; 67*8005SMark.Powers@Sun.COM } else { 68*8005SMark.Powers@Sun.COM v.b = (v.a << 63)|(v.b >> 1); 69*8005SMark.Powers@Sun.COM v.a = v.a >> 1; 70*8005SMark.Powers@Sun.COM } 71*8005SMark.Powers@Sun.COM } 72*8005SMark.Powers@Sun.COM } 73*8005SMark.Powers@Sun.COM res[0] = htonll(z.a); 74*8005SMark.Powers@Sun.COM res[1] = htonll(z.b); 75*8005SMark.Powers@Sun.COM } 76*8005SMark.Powers@Sun.COM 77*8005SMark.Powers@Sun.COM #define GHASH(c, d, t) \ 78*8005SMark.Powers@Sun.COM xor_block((uint8_t *)(d), (uint8_t *)(c)->gcm_ghash); \ 79*8005SMark.Powers@Sun.COM gcm_mul((uint64_t *)(c)->gcm_ghash, (c)->gcm_H, (uint64_t *)(t)); 80*8005SMark.Powers@Sun.COM 81*8005SMark.Powers@Sun.COM /* 82*8005SMark.Powers@Sun.COM * Encrypt multiple blocks of data in GCM mode. Decrypt for GCM mode 83*8005SMark.Powers@Sun.COM * is done in another function. 84*8005SMark.Powers@Sun.COM */ 85*8005SMark.Powers@Sun.COM int 86*8005SMark.Powers@Sun.COM gcm_mode_encrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 87*8005SMark.Powers@Sun.COM crypto_data_t *out, size_t block_size, 88*8005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 89*8005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 90*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 91*8005SMark.Powers@Sun.COM { 92*8005SMark.Powers@Sun.COM size_t remainder = length; 93*8005SMark.Powers@Sun.COM size_t need; 94*8005SMark.Powers@Sun.COM uint8_t *datap = (uint8_t *)data; 95*8005SMark.Powers@Sun.COM uint8_t *blockp; 96*8005SMark.Powers@Sun.COM uint8_t *lastp; 97*8005SMark.Powers@Sun.COM void *iov_or_mp; 98*8005SMark.Powers@Sun.COM offset_t offset; 99*8005SMark.Powers@Sun.COM uint8_t *out_data_1; 100*8005SMark.Powers@Sun.COM uint8_t *out_data_2; 101*8005SMark.Powers@Sun.COM size_t out_data_1_len; 102*8005SMark.Powers@Sun.COM uint64_t counter; 103*8005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 104*8005SMark.Powers@Sun.COM 105*8005SMark.Powers@Sun.COM if (length + ctx->gcm_remainder_len < block_size) { 106*8005SMark.Powers@Sun.COM /* accumulate bytes here and return */ 107*8005SMark.Powers@Sun.COM bcopy(datap, 108*8005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_remainder + ctx->gcm_remainder_len, 109*8005SMark.Powers@Sun.COM length); 110*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len += length; 111*8005SMark.Powers@Sun.COM ctx->gcm_copy_to = datap; 112*8005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 113*8005SMark.Powers@Sun.COM } 114*8005SMark.Powers@Sun.COM 115*8005SMark.Powers@Sun.COM lastp = (uint8_t *)ctx->gcm_cb; 116*8005SMark.Powers@Sun.COM if (out != NULL) 117*8005SMark.Powers@Sun.COM crypto_init_ptrs(out, &iov_or_mp, &offset); 118*8005SMark.Powers@Sun.COM 119*8005SMark.Powers@Sun.COM do { 120*8005SMark.Powers@Sun.COM /* Unprocessed data from last call. */ 121*8005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 122*8005SMark.Powers@Sun.COM need = block_size - ctx->gcm_remainder_len; 123*8005SMark.Powers@Sun.COM 124*8005SMark.Powers@Sun.COM if (need > remainder) 125*8005SMark.Powers@Sun.COM return (CRYPTO_DATA_LEN_RANGE); 126*8005SMark.Powers@Sun.COM 127*8005SMark.Powers@Sun.COM bcopy(datap, &((uint8_t *)ctx->gcm_remainder) 128*8005SMark.Powers@Sun.COM [ctx->gcm_remainder_len], need); 129*8005SMark.Powers@Sun.COM 130*8005SMark.Powers@Sun.COM blockp = (uint8_t *)ctx->gcm_remainder; 131*8005SMark.Powers@Sun.COM } else { 132*8005SMark.Powers@Sun.COM blockp = datap; 133*8005SMark.Powers@Sun.COM } 134*8005SMark.Powers@Sun.COM 135*8005SMark.Powers@Sun.COM /* 136*8005SMark.Powers@Sun.COM * Increment counter. Counter bits are confined 137*8005SMark.Powers@Sun.COM * to the bottom 32 bits of the counter block. 138*8005SMark.Powers@Sun.COM */ 139*8005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 140*8005SMark.Powers@Sun.COM counter = htonll(counter + 1); 141*8005SMark.Powers@Sun.COM counter &= counter_mask; 142*8005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 143*8005SMark.Powers@Sun.COM 144*8005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 145*8005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_tmp); 146*8005SMark.Powers@Sun.COM xor_block(blockp, (uint8_t *)ctx->gcm_tmp); 147*8005SMark.Powers@Sun.COM 148*8005SMark.Powers@Sun.COM lastp = (uint8_t *)ctx->gcm_tmp; 149*8005SMark.Powers@Sun.COM 150*8005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += block_size; 151*8005SMark.Powers@Sun.COM 152*8005SMark.Powers@Sun.COM if (out == NULL) { 153*8005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 154*8005SMark.Powers@Sun.COM bcopy(blockp, ctx->gcm_copy_to, 155*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len); 156*8005SMark.Powers@Sun.COM bcopy(blockp + ctx->gcm_remainder_len, datap, 157*8005SMark.Powers@Sun.COM need); 158*8005SMark.Powers@Sun.COM } 159*8005SMark.Powers@Sun.COM } else { 160*8005SMark.Powers@Sun.COM crypto_get_ptrs(out, &iov_or_mp, &offset, &out_data_1, 161*8005SMark.Powers@Sun.COM &out_data_1_len, &out_data_2, block_size); 162*8005SMark.Powers@Sun.COM 163*8005SMark.Powers@Sun.COM /* copy block to where it belongs */ 164*8005SMark.Powers@Sun.COM if (out_data_1_len == block_size) { 165*8005SMark.Powers@Sun.COM copy_block(lastp, out_data_1); 166*8005SMark.Powers@Sun.COM } else { 167*8005SMark.Powers@Sun.COM bcopy(lastp, out_data_1, out_data_1_len); 168*8005SMark.Powers@Sun.COM if (out_data_2 != NULL) { 169*8005SMark.Powers@Sun.COM bcopy(lastp + out_data_1_len, 170*8005SMark.Powers@Sun.COM out_data_2, 171*8005SMark.Powers@Sun.COM block_size - out_data_1_len); 172*8005SMark.Powers@Sun.COM } 173*8005SMark.Powers@Sun.COM } 174*8005SMark.Powers@Sun.COM /* update offset */ 175*8005SMark.Powers@Sun.COM out->cd_offset += block_size; 176*8005SMark.Powers@Sun.COM } 177*8005SMark.Powers@Sun.COM 178*8005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 179*8005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 180*8005SMark.Powers@Sun.COM 181*8005SMark.Powers@Sun.COM /* Update pointer to next block of data to be processed. */ 182*8005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len != 0) { 183*8005SMark.Powers@Sun.COM datap += need; 184*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 185*8005SMark.Powers@Sun.COM } else { 186*8005SMark.Powers@Sun.COM datap += block_size; 187*8005SMark.Powers@Sun.COM } 188*8005SMark.Powers@Sun.COM 189*8005SMark.Powers@Sun.COM remainder = (size_t)&data[length] - (size_t)datap; 190*8005SMark.Powers@Sun.COM 191*8005SMark.Powers@Sun.COM /* Incomplete last block. */ 192*8005SMark.Powers@Sun.COM if (remainder > 0 && remainder < block_size) { 193*8005SMark.Powers@Sun.COM bcopy(datap, ctx->gcm_remainder, remainder); 194*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len = remainder; 195*8005SMark.Powers@Sun.COM ctx->gcm_copy_to = datap; 196*8005SMark.Powers@Sun.COM goto out; 197*8005SMark.Powers@Sun.COM } 198*8005SMark.Powers@Sun.COM ctx->gcm_copy_to = NULL; 199*8005SMark.Powers@Sun.COM 200*8005SMark.Powers@Sun.COM } while (remainder > 0); 201*8005SMark.Powers@Sun.COM out: 202*8005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 203*8005SMark.Powers@Sun.COM } 204*8005SMark.Powers@Sun.COM 205*8005SMark.Powers@Sun.COM /* ARGSUSED */ 206*8005SMark.Powers@Sun.COM int 207*8005SMark.Powers@Sun.COM gcm_encrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 208*8005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 209*8005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 210*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 211*8005SMark.Powers@Sun.COM { 212*8005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 213*8005SMark.Powers@Sun.COM uint8_t *ghash, *macp; 214*8005SMark.Powers@Sun.COM int i, rv; 215*8005SMark.Powers@Sun.COM 216*8005SMark.Powers@Sun.COM if (out->cd_length < 217*8005SMark.Powers@Sun.COM (ctx->gcm_remainder_len + ctx->gcm_tag_len)) { 218*8005SMark.Powers@Sun.COM return (CRYPTO_DATA_LEN_RANGE); 219*8005SMark.Powers@Sun.COM } 220*8005SMark.Powers@Sun.COM 221*8005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 222*8005SMark.Powers@Sun.COM 223*8005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 224*8005SMark.Powers@Sun.COM uint64_t counter; 225*8005SMark.Powers@Sun.COM uint8_t *tmpp = (uint8_t *)ctx->gcm_tmp; 226*8005SMark.Powers@Sun.COM 227*8005SMark.Powers@Sun.COM /* 228*8005SMark.Powers@Sun.COM * Here is where we deal with data that is not a 229*8005SMark.Powers@Sun.COM * multiple of the block size. 230*8005SMark.Powers@Sun.COM */ 231*8005SMark.Powers@Sun.COM 232*8005SMark.Powers@Sun.COM /* 233*8005SMark.Powers@Sun.COM * Increment counter. 234*8005SMark.Powers@Sun.COM */ 235*8005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 236*8005SMark.Powers@Sun.COM counter = htonll(counter + 1); 237*8005SMark.Powers@Sun.COM counter &= counter_mask; 238*8005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 239*8005SMark.Powers@Sun.COM 240*8005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, 241*8005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_tmp); 242*8005SMark.Powers@Sun.COM 243*8005SMark.Powers@Sun.COM macp = (uint8_t *)ctx->gcm_remainder; 244*8005SMark.Powers@Sun.COM bzero(macp + ctx->gcm_remainder_len, 245*8005SMark.Powers@Sun.COM block_size - ctx->gcm_remainder_len); 246*8005SMark.Powers@Sun.COM 247*8005SMark.Powers@Sun.COM /* XOR with counter block */ 248*8005SMark.Powers@Sun.COM for (i = 0; i < ctx->gcm_remainder_len; i++) { 249*8005SMark.Powers@Sun.COM macp[i] ^= tmpp[i]; 250*8005SMark.Powers@Sun.COM } 251*8005SMark.Powers@Sun.COM 252*8005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 253*8005SMark.Powers@Sun.COM GHASH(ctx, macp, ghash); 254*8005SMark.Powers@Sun.COM 255*8005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += ctx->gcm_remainder_len; 256*8005SMark.Powers@Sun.COM } 257*8005SMark.Powers@Sun.COM 258*8005SMark.Powers@Sun.COM ctx->gcm_len_a_len_c[1] = htonll(ctx->gcm_processed_data_len << 3); 259*8005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 260*8005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 261*8005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_J0); 262*8005SMark.Powers@Sun.COM xor_block((uint8_t *)ctx->gcm_J0, ghash); 263*8005SMark.Powers@Sun.COM 264*8005SMark.Powers@Sun.COM if (ctx->gcm_remainder_len > 0) { 265*8005SMark.Powers@Sun.COM rv = crypto_put_output_data(macp, out, ctx->gcm_remainder_len); 266*8005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 267*8005SMark.Powers@Sun.COM return (rv); 268*8005SMark.Powers@Sun.COM } 269*8005SMark.Powers@Sun.COM out->cd_offset += ctx->gcm_remainder_len; 270*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 271*8005SMark.Powers@Sun.COM rv = crypto_put_output_data(ghash, out, ctx->gcm_tag_len); 272*8005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 273*8005SMark.Powers@Sun.COM return (rv); 274*8005SMark.Powers@Sun.COM out->cd_offset += ctx->gcm_tag_len; 275*8005SMark.Powers@Sun.COM 276*8005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 277*8005SMark.Powers@Sun.COM } 278*8005SMark.Powers@Sun.COM 279*8005SMark.Powers@Sun.COM /* 280*8005SMark.Powers@Sun.COM * This will only deal with decrypting the last block of the input that 281*8005SMark.Powers@Sun.COM * might not be a multiple of block length. 282*8005SMark.Powers@Sun.COM */ 283*8005SMark.Powers@Sun.COM static void 284*8005SMark.Powers@Sun.COM gcm_decrypt_incomplete_block(gcm_ctx_t *ctx, size_t block_size, size_t index, 285*8005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 286*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 287*8005SMark.Powers@Sun.COM { 288*8005SMark.Powers@Sun.COM uint8_t *datap, *outp, *counterp; 289*8005SMark.Powers@Sun.COM uint64_t counter; 290*8005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 291*8005SMark.Powers@Sun.COM int i; 292*8005SMark.Powers@Sun.COM 293*8005SMark.Powers@Sun.COM /* 294*8005SMark.Powers@Sun.COM * Increment counter. 295*8005SMark.Powers@Sun.COM * Counter bits are confined to the bottom 32 bits 296*8005SMark.Powers@Sun.COM */ 297*8005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 298*8005SMark.Powers@Sun.COM counter = htonll(counter + 1); 299*8005SMark.Powers@Sun.COM counter &= counter_mask; 300*8005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 301*8005SMark.Powers@Sun.COM 302*8005SMark.Powers@Sun.COM datap = (uint8_t *)ctx->gcm_remainder; 303*8005SMark.Powers@Sun.COM outp = &((ctx->gcm_pt_buf)[index]); 304*8005SMark.Powers@Sun.COM counterp = (uint8_t *)ctx->gcm_tmp; 305*8005SMark.Powers@Sun.COM 306*8005SMark.Powers@Sun.COM /* authentication tag */ 307*8005SMark.Powers@Sun.COM bzero((uint8_t *)ctx->gcm_tmp, block_size); 308*8005SMark.Powers@Sun.COM bcopy(datap, (uint8_t *)ctx->gcm_tmp, ctx->gcm_remainder_len); 309*8005SMark.Powers@Sun.COM 310*8005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 311*8005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_tmp, ctx->gcm_ghash); 312*8005SMark.Powers@Sun.COM 313*8005SMark.Powers@Sun.COM /* decrypt remaining ciphertext */ 314*8005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, counterp); 315*8005SMark.Powers@Sun.COM 316*8005SMark.Powers@Sun.COM /* XOR with counter block */ 317*8005SMark.Powers@Sun.COM for (i = 0; i < ctx->gcm_remainder_len; i++) { 318*8005SMark.Powers@Sun.COM outp[i] = datap[i] ^ counterp[i]; 319*8005SMark.Powers@Sun.COM } 320*8005SMark.Powers@Sun.COM } 321*8005SMark.Powers@Sun.COM 322*8005SMark.Powers@Sun.COM /* ARGSUSED */ 323*8005SMark.Powers@Sun.COM int 324*8005SMark.Powers@Sun.COM gcm_mode_decrypt_contiguous_blocks(gcm_ctx_t *ctx, char *data, size_t length, 325*8005SMark.Powers@Sun.COM crypto_data_t *out, size_t block_size, 326*8005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 327*8005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 328*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 329*8005SMark.Powers@Sun.COM { 330*8005SMark.Powers@Sun.COM size_t new_len; 331*8005SMark.Powers@Sun.COM uint8_t *new; 332*8005SMark.Powers@Sun.COM 333*8005SMark.Powers@Sun.COM /* 334*8005SMark.Powers@Sun.COM * Copy contiguous ciphertext input blocks to plaintext buffer. 335*8005SMark.Powers@Sun.COM * Ciphertext will be decrypted in the final. 336*8005SMark.Powers@Sun.COM */ 337*8005SMark.Powers@Sun.COM if (length > 0) { 338*8005SMark.Powers@Sun.COM new_len = ctx->gcm_pt_buf_len + length; 339*8005SMark.Powers@Sun.COM #ifdef _KERNEL 340*8005SMark.Powers@Sun.COM new = kmem_alloc(new_len, ctx->gcm_kmflag); 341*8005SMark.Powers@Sun.COM bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 342*8005SMark.Powers@Sun.COM kmem_free(ctx->gcm_pt_buf, ctx->gcm_pt_buf_len); 343*8005SMark.Powers@Sun.COM #else 344*8005SMark.Powers@Sun.COM new = malloc(new_len); 345*8005SMark.Powers@Sun.COM bcopy(ctx->gcm_pt_buf, new, ctx->gcm_pt_buf_len); 346*8005SMark.Powers@Sun.COM free(ctx->gcm_pt_buf); 347*8005SMark.Powers@Sun.COM #endif 348*8005SMark.Powers@Sun.COM if (new == NULL) 349*8005SMark.Powers@Sun.COM return (CRYPTO_HOST_MEMORY); 350*8005SMark.Powers@Sun.COM 351*8005SMark.Powers@Sun.COM ctx->gcm_pt_buf = new; 352*8005SMark.Powers@Sun.COM ctx->gcm_pt_buf_len = new_len; 353*8005SMark.Powers@Sun.COM bcopy(data, &ctx->gcm_pt_buf[ctx->gcm_processed_data_len], 354*8005SMark.Powers@Sun.COM length); 355*8005SMark.Powers@Sun.COM ctx->gcm_processed_data_len += length; 356*8005SMark.Powers@Sun.COM } 357*8005SMark.Powers@Sun.COM 358*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 359*8005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 360*8005SMark.Powers@Sun.COM } 361*8005SMark.Powers@Sun.COM 362*8005SMark.Powers@Sun.COM int 363*8005SMark.Powers@Sun.COM gcm_decrypt_final(gcm_ctx_t *ctx, crypto_data_t *out, size_t block_size, 364*8005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 365*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 366*8005SMark.Powers@Sun.COM { 367*8005SMark.Powers@Sun.COM size_t pt_len; 368*8005SMark.Powers@Sun.COM size_t remainder; 369*8005SMark.Powers@Sun.COM uint8_t *ghash; 370*8005SMark.Powers@Sun.COM uint8_t *blockp; 371*8005SMark.Powers@Sun.COM uint8_t *cbp; 372*8005SMark.Powers@Sun.COM uint64_t counter; 373*8005SMark.Powers@Sun.COM uint64_t counter_mask = ntohll(0x00000000ffffffffULL); 374*8005SMark.Powers@Sun.COM int processed = 0, rv; 375*8005SMark.Powers@Sun.COM 376*8005SMark.Powers@Sun.COM ASSERT(ctx->gcm_processed_data_len == ctx->gcm_pt_buf_len); 377*8005SMark.Powers@Sun.COM 378*8005SMark.Powers@Sun.COM pt_len = ctx->gcm_processed_data_len - ctx->gcm_tag_len; 379*8005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 380*8005SMark.Powers@Sun.COM blockp = ctx->gcm_pt_buf; 381*8005SMark.Powers@Sun.COM remainder = pt_len; 382*8005SMark.Powers@Sun.COM while (remainder > 0) { 383*8005SMark.Powers@Sun.COM /* add ciphertext to the hash */ 384*8005SMark.Powers@Sun.COM GHASH(ctx, blockp, ghash); 385*8005SMark.Powers@Sun.COM 386*8005SMark.Powers@Sun.COM /* 387*8005SMark.Powers@Sun.COM * Increment counter. 388*8005SMark.Powers@Sun.COM * Counter bits are confined to the bottom 32 bits 389*8005SMark.Powers@Sun.COM */ 390*8005SMark.Powers@Sun.COM counter = ntohll(ctx->gcm_cb[1] & counter_mask); 391*8005SMark.Powers@Sun.COM counter = htonll(counter + 1); 392*8005SMark.Powers@Sun.COM counter &= counter_mask; 393*8005SMark.Powers@Sun.COM ctx->gcm_cb[1] = (ctx->gcm_cb[1] & ~counter_mask) | counter; 394*8005SMark.Powers@Sun.COM 395*8005SMark.Powers@Sun.COM cbp = (uint8_t *)ctx->gcm_tmp; 396*8005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_cb, cbp); 397*8005SMark.Powers@Sun.COM 398*8005SMark.Powers@Sun.COM /* XOR with ciphertext */ 399*8005SMark.Powers@Sun.COM xor_block(cbp, blockp); 400*8005SMark.Powers@Sun.COM 401*8005SMark.Powers@Sun.COM processed += block_size; 402*8005SMark.Powers@Sun.COM blockp += block_size; 403*8005SMark.Powers@Sun.COM remainder -= block_size; 404*8005SMark.Powers@Sun.COM 405*8005SMark.Powers@Sun.COM /* Incomplete last block */ 406*8005SMark.Powers@Sun.COM if (remainder > 0 && remainder < block_size) { 407*8005SMark.Powers@Sun.COM bcopy(blockp, ctx->gcm_remainder, remainder); 408*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len = remainder; 409*8005SMark.Powers@Sun.COM /* 410*8005SMark.Powers@Sun.COM * not expecting anymore ciphertext, just 411*8005SMark.Powers@Sun.COM * compute plaintext for the remaining input 412*8005SMark.Powers@Sun.COM */ 413*8005SMark.Powers@Sun.COM gcm_decrypt_incomplete_block(ctx, block_size, 414*8005SMark.Powers@Sun.COM processed, encrypt_block, xor_block); 415*8005SMark.Powers@Sun.COM ctx->gcm_remainder_len = 0; 416*8005SMark.Powers@Sun.COM goto out; 417*8005SMark.Powers@Sun.COM } 418*8005SMark.Powers@Sun.COM } 419*8005SMark.Powers@Sun.COM out: 420*8005SMark.Powers@Sun.COM ctx->gcm_len_a_len_c[1] = htonll(pt_len << 3); 421*8005SMark.Powers@Sun.COM GHASH(ctx, ctx->gcm_len_a_len_c, ghash); 422*8005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_J0, 423*8005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_J0); 424*8005SMark.Powers@Sun.COM xor_block((uint8_t *)ctx->gcm_J0, ghash); 425*8005SMark.Powers@Sun.COM 426*8005SMark.Powers@Sun.COM /* compare the input authentication tag with what we calculated */ 427*8005SMark.Powers@Sun.COM if (bcmp(&ctx->gcm_pt_buf[pt_len], ghash, ctx->gcm_tag_len)) { 428*8005SMark.Powers@Sun.COM /* They don't match */ 429*8005SMark.Powers@Sun.COM return (CRYPTO_INVALID_MAC); 430*8005SMark.Powers@Sun.COM } else { 431*8005SMark.Powers@Sun.COM rv = crypto_put_output_data(ctx->gcm_pt_buf, out, pt_len); 432*8005SMark.Powers@Sun.COM if (rv != CRYPTO_SUCCESS) 433*8005SMark.Powers@Sun.COM return (rv); 434*8005SMark.Powers@Sun.COM out->cd_offset += pt_len; 435*8005SMark.Powers@Sun.COM } 436*8005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 437*8005SMark.Powers@Sun.COM } 438*8005SMark.Powers@Sun.COM 439*8005SMark.Powers@Sun.COM static int 440*8005SMark.Powers@Sun.COM gcm_validate_args(CK_AES_GCM_PARAMS *gcm_param) 441*8005SMark.Powers@Sun.COM { 442*8005SMark.Powers@Sun.COM size_t tag_len; 443*8005SMark.Powers@Sun.COM 444*8005SMark.Powers@Sun.COM /* 445*8005SMark.Powers@Sun.COM * Check the length of the authentication tag (in bits). 446*8005SMark.Powers@Sun.COM */ 447*8005SMark.Powers@Sun.COM tag_len = gcm_param->ulTagBits; 448*8005SMark.Powers@Sun.COM switch (tag_len) { 449*8005SMark.Powers@Sun.COM case 32: 450*8005SMark.Powers@Sun.COM case 64: 451*8005SMark.Powers@Sun.COM case 96: 452*8005SMark.Powers@Sun.COM case 104: 453*8005SMark.Powers@Sun.COM case 112: 454*8005SMark.Powers@Sun.COM case 120: 455*8005SMark.Powers@Sun.COM case 128: 456*8005SMark.Powers@Sun.COM break; 457*8005SMark.Powers@Sun.COM default: 458*8005SMark.Powers@Sun.COM return (CRYPTO_MECHANISM_PARAM_INVALID); 459*8005SMark.Powers@Sun.COM } 460*8005SMark.Powers@Sun.COM 461*8005SMark.Powers@Sun.COM if (gcm_param->ulIvLen == 0) 462*8005SMark.Powers@Sun.COM return (CRYPTO_MECHANISM_PARAM_INVALID); 463*8005SMark.Powers@Sun.COM 464*8005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 465*8005SMark.Powers@Sun.COM } 466*8005SMark.Powers@Sun.COM 467*8005SMark.Powers@Sun.COM static void 468*8005SMark.Powers@Sun.COM gcm_format_initial_blocks(uchar_t *iv, ulong_t iv_len, 469*8005SMark.Powers@Sun.COM gcm_ctx_t *ctx, size_t block_size, 470*8005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 471*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 472*8005SMark.Powers@Sun.COM { 473*8005SMark.Powers@Sun.COM uint8_t *cb; 474*8005SMark.Powers@Sun.COM ulong_t remainder = iv_len; 475*8005SMark.Powers@Sun.COM ulong_t processed = 0; 476*8005SMark.Powers@Sun.COM uint8_t *datap, *ghash; 477*8005SMark.Powers@Sun.COM uint64_t len_a_len_c[2]; 478*8005SMark.Powers@Sun.COM 479*8005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 480*8005SMark.Powers@Sun.COM cb = (uint8_t *)ctx->gcm_cb; 481*8005SMark.Powers@Sun.COM if (iv_len == 12) { 482*8005SMark.Powers@Sun.COM bcopy(iv, cb, 12); 483*8005SMark.Powers@Sun.COM cb[12] = 0; 484*8005SMark.Powers@Sun.COM cb[13] = 0; 485*8005SMark.Powers@Sun.COM cb[14] = 0; 486*8005SMark.Powers@Sun.COM cb[15] = 1; 487*8005SMark.Powers@Sun.COM /* J0 will be used again in the final */ 488*8005SMark.Powers@Sun.COM copy_block(cb, (uint8_t *)ctx->gcm_J0); 489*8005SMark.Powers@Sun.COM } else { 490*8005SMark.Powers@Sun.COM /* GHASH the IV */ 491*8005SMark.Powers@Sun.COM do { 492*8005SMark.Powers@Sun.COM if (remainder < block_size) { 493*8005SMark.Powers@Sun.COM bzero(cb, block_size); 494*8005SMark.Powers@Sun.COM bcopy(&(iv[processed]), cb, remainder); 495*8005SMark.Powers@Sun.COM datap = (uint8_t *)cb; 496*8005SMark.Powers@Sun.COM remainder = 0; 497*8005SMark.Powers@Sun.COM } else { 498*8005SMark.Powers@Sun.COM datap = (uint8_t *)(&(iv[processed])); 499*8005SMark.Powers@Sun.COM processed += block_size; 500*8005SMark.Powers@Sun.COM remainder -= block_size; 501*8005SMark.Powers@Sun.COM } 502*8005SMark.Powers@Sun.COM GHASH(ctx, datap, ghash); 503*8005SMark.Powers@Sun.COM } while (remainder > 0); 504*8005SMark.Powers@Sun.COM 505*8005SMark.Powers@Sun.COM len_a_len_c[0] = 0; 506*8005SMark.Powers@Sun.COM len_a_len_c[1] = htonll(iv_len << 3); 507*8005SMark.Powers@Sun.COM GHASH(ctx, len_a_len_c, ctx->gcm_J0); 508*8005SMark.Powers@Sun.COM 509*8005SMark.Powers@Sun.COM /* J0 will be used again in the final */ 510*8005SMark.Powers@Sun.COM copy_block((uint8_t *)ctx->gcm_J0, (uint8_t *)cb); 511*8005SMark.Powers@Sun.COM } 512*8005SMark.Powers@Sun.COM } 513*8005SMark.Powers@Sun.COM 514*8005SMark.Powers@Sun.COM /* 515*8005SMark.Powers@Sun.COM * The following function is called at encrypt or decrypt init time 516*8005SMark.Powers@Sun.COM * for AES GCM mode. 517*8005SMark.Powers@Sun.COM */ 518*8005SMark.Powers@Sun.COM int 519*8005SMark.Powers@Sun.COM gcm_init(gcm_ctx_t *ctx, unsigned char *iv, size_t iv_len, 520*8005SMark.Powers@Sun.COM unsigned char *auth_data, size_t auth_data_len, size_t block_size, 521*8005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 522*8005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 523*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 524*8005SMark.Powers@Sun.COM { 525*8005SMark.Powers@Sun.COM uint8_t *ghash, *datap, *authp; 526*8005SMark.Powers@Sun.COM size_t remainder, processed; 527*8005SMark.Powers@Sun.COM 528*8005SMark.Powers@Sun.COM /* encrypt zero block to get subkey H */ 529*8005SMark.Powers@Sun.COM bzero(ctx->gcm_H, sizeof (ctx->gcm_H)); 530*8005SMark.Powers@Sun.COM encrypt_block(ctx->gcm_keysched, (uint8_t *)ctx->gcm_H, 531*8005SMark.Powers@Sun.COM (uint8_t *)ctx->gcm_H); 532*8005SMark.Powers@Sun.COM 533*8005SMark.Powers@Sun.COM gcm_format_initial_blocks(iv, iv_len, ctx, block_size, 534*8005SMark.Powers@Sun.COM copy_block, xor_block); 535*8005SMark.Powers@Sun.COM 536*8005SMark.Powers@Sun.COM authp = (uint8_t *)ctx->gcm_tmp; 537*8005SMark.Powers@Sun.COM ghash = (uint8_t *)ctx->gcm_ghash; 538*8005SMark.Powers@Sun.COM bzero(authp, block_size); 539*8005SMark.Powers@Sun.COM bzero(ghash, block_size); 540*8005SMark.Powers@Sun.COM 541*8005SMark.Powers@Sun.COM processed = 0; 542*8005SMark.Powers@Sun.COM remainder = auth_data_len; 543*8005SMark.Powers@Sun.COM do { 544*8005SMark.Powers@Sun.COM if (remainder < block_size) { 545*8005SMark.Powers@Sun.COM /* 546*8005SMark.Powers@Sun.COM * There's not a block full of data, pad rest of 547*8005SMark.Powers@Sun.COM * buffer with zero 548*8005SMark.Powers@Sun.COM */ 549*8005SMark.Powers@Sun.COM bzero(authp, block_size); 550*8005SMark.Powers@Sun.COM bcopy(&(auth_data[processed]), authp, remainder); 551*8005SMark.Powers@Sun.COM datap = (uint8_t *)authp; 552*8005SMark.Powers@Sun.COM remainder = 0; 553*8005SMark.Powers@Sun.COM } else { 554*8005SMark.Powers@Sun.COM datap = (uint8_t *)(&(auth_data[processed])); 555*8005SMark.Powers@Sun.COM processed += block_size; 556*8005SMark.Powers@Sun.COM remainder -= block_size; 557*8005SMark.Powers@Sun.COM } 558*8005SMark.Powers@Sun.COM 559*8005SMark.Powers@Sun.COM /* add auth data to the hash */ 560*8005SMark.Powers@Sun.COM GHASH(ctx, datap, ghash); 561*8005SMark.Powers@Sun.COM 562*8005SMark.Powers@Sun.COM } while (remainder > 0); 563*8005SMark.Powers@Sun.COM 564*8005SMark.Powers@Sun.COM return (CRYPTO_SUCCESS); 565*8005SMark.Powers@Sun.COM } 566*8005SMark.Powers@Sun.COM 567*8005SMark.Powers@Sun.COM int 568*8005SMark.Powers@Sun.COM gcm_init_ctx(gcm_ctx_t *gcm_ctx, char *param, size_t block_size, 569*8005SMark.Powers@Sun.COM int (*encrypt_block)(const void *, const uint8_t *, uint8_t *), 570*8005SMark.Powers@Sun.COM void (*copy_block)(uint8_t *, uint8_t *), 571*8005SMark.Powers@Sun.COM void (*xor_block)(uint8_t *, uint8_t *)) 572*8005SMark.Powers@Sun.COM { 573*8005SMark.Powers@Sun.COM int rv; 574*8005SMark.Powers@Sun.COM CK_AES_GCM_PARAMS *gcm_param; 575*8005SMark.Powers@Sun.COM 576*8005SMark.Powers@Sun.COM if (param != NULL) { 577*8005SMark.Powers@Sun.COM gcm_param = (CK_AES_GCM_PARAMS *)param; 578*8005SMark.Powers@Sun.COM 579*8005SMark.Powers@Sun.COM if ((rv = gcm_validate_args(gcm_param)) != 0) { 580*8005SMark.Powers@Sun.COM return (rv); 581*8005SMark.Powers@Sun.COM } 582*8005SMark.Powers@Sun.COM 583*8005SMark.Powers@Sun.COM gcm_ctx->gcm_tag_len = gcm_param->ulTagBits; 584*8005SMark.Powers@Sun.COM gcm_ctx->gcm_tag_len >>= 3; 585*8005SMark.Powers@Sun.COM gcm_ctx->gcm_processed_data_len = 0; 586*8005SMark.Powers@Sun.COM 587*8005SMark.Powers@Sun.COM /* these values are in bits */ 588*8005SMark.Powers@Sun.COM gcm_ctx->gcm_len_a_len_c[0] = htonll(gcm_param->ulAADLen << 3); 589*8005SMark.Powers@Sun.COM 590*8005SMark.Powers@Sun.COM rv = CRYPTO_SUCCESS; 591*8005SMark.Powers@Sun.COM gcm_ctx->gcm_flags |= GCM_MODE; 592*8005SMark.Powers@Sun.COM } else { 593*8005SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 594*8005SMark.Powers@Sun.COM goto out; 595*8005SMark.Powers@Sun.COM } 596*8005SMark.Powers@Sun.COM 597*8005SMark.Powers@Sun.COM if (gcm_init(gcm_ctx, gcm_param->pIv, gcm_param->ulIvLen, 598*8005SMark.Powers@Sun.COM gcm_param->pAAD, gcm_param->ulAADLen, block_size, 599*8005SMark.Powers@Sun.COM encrypt_block, copy_block, xor_block) != 0) { 600*8005SMark.Powers@Sun.COM rv = CRYPTO_MECHANISM_PARAM_INVALID; 601*8005SMark.Powers@Sun.COM } 602*8005SMark.Powers@Sun.COM out: 603*8005SMark.Powers@Sun.COM return (rv); 604*8005SMark.Powers@Sun.COM } 605*8005SMark.Powers@Sun.COM 606*8005SMark.Powers@Sun.COM void * 607*8005SMark.Powers@Sun.COM gcm_alloc_ctx(int kmflag) 608*8005SMark.Powers@Sun.COM { 609*8005SMark.Powers@Sun.COM gcm_ctx_t *gcm_ctx; 610*8005SMark.Powers@Sun.COM 611*8005SMark.Powers@Sun.COM #ifdef _KERNEL 612*8005SMark.Powers@Sun.COM if ((gcm_ctx = kmem_zalloc(sizeof (gcm_ctx_t), kmflag)) == NULL) 613*8005SMark.Powers@Sun.COM #else 614*8005SMark.Powers@Sun.COM if ((gcm_ctx = calloc(1, sizeof (gcm_ctx_t))) == NULL) 615*8005SMark.Powers@Sun.COM #endif 616*8005SMark.Powers@Sun.COM return (NULL); 617*8005SMark.Powers@Sun.COM 618*8005SMark.Powers@Sun.COM gcm_ctx->gcm_flags = GCM_MODE; 619*8005SMark.Powers@Sun.COM return (gcm_ctx); 620*8005SMark.Powers@Sun.COM } 621*8005SMark.Powers@Sun.COM 622*8005SMark.Powers@Sun.COM void 623*8005SMark.Powers@Sun.COM gcm_set_kmflag(gcm_ctx_t *ctx, int kmflag) 624*8005SMark.Powers@Sun.COM { 625*8005SMark.Powers@Sun.COM ctx->gcm_kmflag = kmflag; 626*8005SMark.Powers@Sun.COM } 627