| /* Copyright 2017 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "dcrypto.h" |
| #include "internal.h" |
| #include "registers.h" |
| |
| #include "endian.h" |
| |
| #include "cryptoc/util.h" |
| |
| static void gcm_mul(uint32_t *counter) |
| { |
| int i; |
| volatile uint32_t *p; |
| |
| /* Set HASH to zero. */ |
| p = GREG32_ADDR(KEYMGR, GCM_HASH_IN0); |
| for (i = 0; i < 4; i++) |
| *p++ = 0; |
| |
| /* Initialize GMAC. */ |
| p = GREG32_ADDR(KEYMGR, GCM_MAC0); |
| for (i = 0; i < 4; i++) |
| *p++ = counter[i]; |
| |
| /* Crank GMAC. */ |
| GREG32(KEYMGR, GCM_DO_ACC) = 1; |
| |
| /* Read GMAC. */ |
| p = GREG32_ADDR(KEYMGR, GCM_MAC0); |
| for (i = 0; i < 4; i++) |
| counter[i] = *p++; |
| |
| /* Reset GMAC. */ |
| p = GREG32_ADDR(KEYMGR, GCM_MAC0); |
| for (i = 0; i < 4; ++i) |
| *p++ = 0; |
| } |
| |
| static void gcm_init_iv( |
| const uint8_t *iv, uint32_t iv_len, uint32_t *counter) |
| { |
| |
| if (iv_len == 12) { |
| memcpy(counter, iv, 12); |
| counter[3] = BIT(24); |
| } else { |
| size_t i; |
| uint32_t len = iv_len; |
| uint64_t len0 = len; |
| uint8_t *ctr = (uint8_t *) counter; |
| |
| memset(ctr, 0, 16); |
| while (len >= 16) { |
| for (i = 0; i < 16; ++i) |
| ctr[i] ^= iv[i]; |
| |
| gcm_mul(counter); |
| iv += 16; |
| len -= 16; |
| } |
| if (len) { |
| for (i = 0; i < len; ++i) |
| ctr[i] ^= iv[i]; |
| |
| gcm_mul(counter); |
| } |
| len0 <<= 3; |
| ctr[8] ^= (uint8_t)(len0 >> 56); |
| ctr[9] ^= (uint8_t)(len0 >> 48); |
| ctr[10] ^= (uint8_t)(len0 >> 40); |
| ctr[11] ^= (uint8_t)(len0 >> 32); |
| ctr[12] ^= (uint8_t)(len0 >> 24); |
| ctr[13] ^= (uint8_t)(len0 >> 16); |
| ctr[14] ^= (uint8_t)(len0 >> 8); |
| ctr[15] ^= (uint8_t)(len0); |
| |
| gcm_mul(counter); |
| } |
| } |
| |
| void DCRYPTO_gcm_init(struct GCM_CTX *ctx, uint32_t key_bits, |
| const uint8_t *key, const uint8_t *iv, size_t iv_len) |
| { |
| int i; |
| const uint32_t zero[4] = {0, 0, 0, 0}; |
| uint32_t H[4]; |
| uint32_t counter[4]; |
| |
| memset(ctx, 0, sizeof(struct GCM_CTX)); |
| |
| /* Initialize AES engine in CTR mode, and set the counter to 0. */ |
| DCRYPTO_aes_init(key, key_bits, (const uint8_t *) zero, |
| CIPHER_MODE_CTR, ENCRYPT_MODE); |
| /* Set H to AES(ZERO). */ |
| DCRYPTO_aes_block((const uint8_t *) zero, (uint8_t *) H); |
| |
| /* Initialize the GMAC accumulator to ZERO. */ |
| for (i = 0; i < 4; i++) |
| GR_KEYMGR_GCM_MAC(i) = zero[i]; |
| |
| /* Initialize H. */ |
| for (i = 0; i < 4; i++) |
| GR_KEYMGR_GCM_H(i) = H[i]; |
| |
| /* Map the IV to a 128-bit counter. */ |
| gcm_init_iv(iv, iv_len, counter); |
| |
| /* Re-initialize the IV counter. */ |
| for (i = 0; i < 4; i++) |
| GR_KEYMGR_AES_CTR(i) = counter[i]; |
| |
| /* Calculate Ej0: encrypt IV counter XOR ZERO. */ |
| DCRYPTO_aes_block((const uint8_t *) zero, ctx->Ej0.c); |
| } |
| |
| static void gcm_aad_block(const struct GCM_CTX *ctx, const uint32_t *block) |
| { |
| int i; |
| const struct access_helper *p = (struct access_helper *) block; |
| |
| if (ctx->aad_len == 0 && ctx->count <= 16) { |
| /* Update GMAC. */ |
| for (i = 0; i < 4; i++) |
| GR_KEYMGR_GCM_MAC(i) = p[i].udata; |
| } else { |
| for (i = 0; i < 4; i++) |
| GR_KEYMGR_GCM_HASH_IN(i) = p[i].udata; |
| |
| /* Crank GMAC. */ |
| GREG32(KEYMGR, GCM_DO_ACC) = 1; |
| } |
| } |
| |
| void DCRYPTO_gcm_aad(struct GCM_CTX *ctx, const uint8_t *aad_data, size_t len) |
| { |
| uint32_t block[4]; |
| |
| while (len) { |
| size_t count; |
| |
| memset(block, 0, sizeof(block)); |
| count = MIN(16, len); |
| memcpy(block, aad_data, count); |
| |
| gcm_aad_block(ctx, block); |
| ctx->aad_len += count; |
| |
| len -= count; |
| aad_data += count; |
| } |
| |
| always_memset(block, 0, sizeof(block)); |
| } |
| |
| int DCRYPTO_gcm_encrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, |
| const uint8_t *in, size_t in_len) |
| { |
| uint8_t *outp = out; |
| |
| if (out_len < (in_len & ~0x0F) + ((in_len & 0x0F) ? 16 : 0)) |
| return -1; |
| |
| /* Process a previous partial block, if any. */ |
| if (ctx->remainder) { |
| size_t count = MIN(in_len, 16 - ctx->remainder); |
| |
| memcpy(ctx->block.c + ctx->remainder, in, count); |
| ctx->remainder += count; |
| if (ctx->remainder < 16) |
| return 0; |
| |
| DCRYPTO_aes_block(ctx->block.c, outp); |
| ctx->count += 16; |
| gcm_aad_block(ctx, (uint32_t *) outp); |
| ctx->remainder = 0; |
| in += count; |
| in_len -= count; |
| outp += 16; |
| } |
| |
| while (in_len >= 16) { |
| DCRYPTO_aes_block(in, outp); |
| ctx->count += 16; |
| |
| gcm_aad_block(ctx, (uint32_t *) outp); |
| |
| in_len -= 16; |
| in += 16; |
| outp += 16; |
| } |
| |
| if (in_len) { |
| memcpy(ctx->block.c, in, in_len); |
| ctx->remainder = in_len; |
| } |
| |
| return outp - out; |
| } |
| |
| int DCRYPTO_gcm_encrypt_final(struct GCM_CTX *ctx, uint8_t *out, size_t out_len) |
| { |
| if (out_len < ctx->remainder) |
| return -1; |
| |
| if (ctx->remainder) { |
| size_t remainder = ctx->remainder; |
| uint8_t out_block[16]; |
| |
| DCRYPTO_aes_block(ctx->block.c, out_block); |
| ctx->count += ctx->remainder; |
| memcpy(out, out_block, ctx->remainder); |
| |
| memset(out_block + ctx->remainder, 0, 16 - ctx->remainder); |
| gcm_aad_block(ctx, (uint32_t *) out_block); |
| ctx->remainder = 0; |
| return remainder; |
| } |
| |
| return 0; |
| } |
| |
| int DCRYPTO_gcm_decrypt(struct GCM_CTX *ctx, uint8_t *out, size_t out_len, |
| const uint8_t *in, size_t in_len) |
| { |
| uint8_t *outp = out; |
| |
| if (out_len < (in_len & ~0x0F) + ((in_len & 0x0F) ? 16 : 0)) |
| return -1; |
| |
| if (ctx->remainder) { |
| size_t count = MIN(in_len, 16 - ctx->remainder); |
| |
| memcpy(ctx->block.c + ctx->remainder, in, count); |
| ctx->remainder += count; |
| |
| if (ctx->remainder < 16) |
| return 0; |
| |
| DCRYPTO_aes_block(ctx->block.c, outp); |
| ctx->remainder = 0; |
| ctx->count += 16; |
| gcm_aad_block(ctx, ctx->block.d); |
| in += count; |
| in_len -= count; |
| outp += count; |
| } |
| |
| while (in_len >= 16) { |
| DCRYPTO_aes_block(in, outp); |
| ctx->count += 16; |
| gcm_aad_block(ctx, (uint32_t *) in); |
| in += 16; |
| in_len -= 16; |
| outp += 16; |
| } |
| |
| if (in_len) { |
| memcpy(ctx->block.c, in, in_len); |
| ctx->remainder = in_len; |
| } |
| |
| return outp - out; |
| } |
| |
| int DCRYPTO_gcm_decrypt_final(struct GCM_CTX *ctx, |
| uint8_t *out, size_t out_len) |
| { |
| if (out_len < ctx->remainder) |
| return -1; |
| |
| if (ctx->remainder) { |
| size_t remainder = ctx->remainder; |
| uint8_t out_block[16]; |
| |
| DCRYPTO_aes_block(ctx->block.c, out_block); |
| ctx->count += ctx->remainder; |
| memcpy(out, out_block, ctx->remainder); |
| |
| memset(ctx->block.c + ctx->remainder, 0, 16 - ctx->remainder); |
| gcm_aad_block(ctx, ctx->block.d); |
| ctx->remainder = 0; |
| return remainder; |
| } |
| |
| return 0; |
| } |
| |
| static void dcrypto_gcm_len_vector( |
| const struct GCM_CTX *ctx, void *len_vector) { |
| uint64_t aad_be; |
| uint64_t count_be; |
| |
| /* Serialize counters to bit-count (big-endian). */ |
| aad_be = ctx->aad_len * 8; |
| aad_be = htobe64(aad_be); |
| count_be = ctx->count * 8; |
| count_be = htobe64(count_be); |
| |
| memcpy(len_vector, &aad_be, 8); |
| memcpy(((uint8_t *)len_vector) + 8, &count_be, 8); |
| } |
| |
| static void dcrypto_gcm_tag(const struct GCM_CTX *ctx, |
| const uint32_t *len_vector, uint32_t *tag) { |
| int i; |
| |
| for (i = 0; i < 4; i++) |
| GR_KEYMGR_GCM_HASH_IN(i) = len_vector[i]; |
| |
| /* Crank GMAC. */ |
| GREG32(KEYMGR, GCM_DO_ACC) = 1; |
| |
| for (i = 0; i < 4; i++) |
| GR_KEYMGR_GCM_HASH_IN(i) = ctx->Ej0.d[i]; |
| |
| /* Crank GMAC. */ |
| GREG32(KEYMGR, GCM_DO_ACC) = 1; |
| |
| /* Read tag. */ |
| for (i = 0; i < 4; i++) |
| tag[i] = GR_KEYMGR_GCM_MAC(i); |
| } |
| |
| int DCRYPTO_gcm_tag(struct GCM_CTX *ctx, uint8_t *tag, size_t tag_len) |
| { |
| uint32_t len_vector[4]; |
| uint32_t local_tag[4]; |
| size_t count = MIN(tag_len, sizeof(local_tag)); |
| |
| dcrypto_gcm_len_vector(ctx, len_vector); |
| dcrypto_gcm_tag(ctx, len_vector, local_tag); |
| |
| memcpy(tag, local_tag, count); |
| return count; |
| } |
| |
| void DCRYPTO_gcm_finish(struct GCM_CTX *ctx) |
| { |
| always_memset(ctx, 0, sizeof(struct GCM_CTX)); |
| GREG32(KEYMGR, AES_WIPE_SECRETS) = 1; |
| } |