summaryrefslogtreecommitdiff
path: root/chip/g/dcrypto/gcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/dcrypto/gcm.c')
-rw-r--r--chip/g/dcrypto/gcm.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/chip/g/dcrypto/gcm.c b/chip/g/dcrypto/gcm.c
new file mode 100644
index 0000000000..cd035bbd54
--- /dev/null
+++ b/chip/g/dcrypto/gcm.c
@@ -0,0 +1,345 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * 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;
+}