| // SPDX-License-Identifier: Apache-2.0 |
| /* |
| * NIST SP800-38D compliant GCM implementation |
| * |
| * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may |
| * not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <io.h> |
| #include <kernel/panic.h> |
| #include <string.h> |
| #include <tee_api_types.h> |
| #include <types_ext.h> |
| |
| #include "aes-gcm-private.h" |
| |
| /* |
| * http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf |
| * |
| * See also: |
| * [MGV] http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/ |
| gcm-revised-spec.pdf |
| * |
| * We use the algorithm described as Shoup's method with 4-bit tables in |
| * [MGV] 4.1, pp. 12-13, to enhance speed without using too much memory. |
| */ |
| |
| /* |
| * Precompute small multiples of H, that is set |
| * HH[i] || HL[i] = H times i, |
| * where i is seen as a field element as in [MGV], ie high-order bits |
| * correspond to low powers of P. The result is stored in the same way, that |
| * is the high-order bit of HH corresponds to P^0 and the low-order bit of HL |
| * corresponds to P^127. |
| */ |
| void internal_aes_gcm_ghash_gen_tbl(struct internal_aes_gcm_state *state, |
| const struct internal_aes_gcm_key *ek) |
| { |
| int i, j; |
| uint64_t vl, vh; |
| unsigned char h[16]; |
| |
| memset(h, 0, 16); |
| internal_aes_gcm_encrypt_block(ek, h, h); |
| |
| vh = get_be64(h); |
| vl = get_be64(h + 8); |
| |
| /* 8 = 1000 corresponds to 1 in GF(2^128) */ |
| state->HL[8] = vl; |
| state->HH[8] = vh; |
| |
| /* 0 corresponds to 0 in GF(2^128) */ |
| state->HH[0] = 0; |
| state->HL[0] = 0; |
| |
| for (i = 4; i > 0; i >>= 1) { |
| uint32_t T = (vl & 1) * 0xe1000000U; |
| |
| vl = (vh << 63) | (vl >> 1); |
| vh = (vh >> 1) ^ ((uint64_t)T << 32); |
| |
| state->HL[i] = vl; |
| state->HH[i] = vh; |
| } |
| |
| for (i = 2; i <= 8; i *= 2) { |
| uint64_t *HiL = state->HL + i, *HiH = state->HH + i; |
| |
| vh = *HiH; |
| vl = *HiL; |
| for (j = 1; j < i; j++) { |
| HiH[j] = vh ^ state->HH[j]; |
| HiL[j] = vl ^ state->HL[j]; |
| } |
| } |
| } |
| |
| /* |
| * Shoup's method for multiplication use this table with |
| * last4[x] = x times P^128 |
| * where x and last4[x] are seen as elements of GF(2^128) as in [MGV] |
| */ |
| static const uint64_t last4[16] = { |
| 0x0000, 0x1c20, 0x3840, 0x2460, |
| 0x7080, 0x6ca0, 0x48c0, 0x54e0, |
| 0xe100, 0xfd20, 0xd940, 0xc560, |
| 0x9180, 0x8da0, 0xa9c0, 0xb5e0 |
| }; |
| |
| /* |
| * Sets output to x times H using the precomputed tables. |
| * x and output are seen as elements of GF(2^128) as in [MGV]. |
| */ |
| static void gcm_mult(struct internal_aes_gcm_state *state, |
| const unsigned char x[16], unsigned char output[16]) |
| { |
| int i = 0; |
| unsigned char lo, hi, rem; |
| uint64_t zh, zl; |
| |
| lo = x[15] & 0xf; |
| |
| zh = state->HH[lo]; |
| zl = state->HL[lo]; |
| |
| for (i = 15; i >= 0; i--) { |
| lo = x[i] & 0xf; |
| hi = x[i] >> 4; |
| |
| if (i != 15) { |
| rem = (unsigned char)zl & 0xf; |
| zl = (zh << 60) | (zl >> 4); |
| zh = (zh >> 4); |
| zh ^= (uint64_t)last4[rem] << 48; |
| zh ^= state->HH[lo]; |
| zl ^= state->HL[lo]; |
| } |
| |
| rem = (unsigned char)zl & 0xf; |
| zl = (zh << 60) | (zl >> 4); |
| zh = (zh >> 4); |
| zh ^= (uint64_t)last4[rem] << 48; |
| zh ^= state->HH[hi]; |
| zl ^= state->HL[hi]; |
| } |
| |
| put_be64(output, zh); |
| put_be64(output + 8, zl); |
| } |
| |
| void internal_aes_gcm_ghash_update_block(struct internal_aes_gcm_state *state, |
| const void *data) |
| { |
| void *y = state->hash_state; |
| |
| internal_aes_gcm_xor_block(y, data); |
| gcm_mult(state, y, y); |
| } |