blob: 203b5300232784a7fbebcb4a6594b9211815941c [file] [log] [blame]
// 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);
}