diff options
Diffstat (limited to 'board/cr50/dcrypto/aes.c')
-rw-r--r-- | board/cr50/dcrypto/aes.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/board/cr50/dcrypto/aes.c b/board/cr50/dcrypto/aes.c new file mode 100644 index 0000000000..f5cc0e6d8f --- /dev/null +++ b/board/cr50/dcrypto/aes.c @@ -0,0 +1,160 @@ +/* Copyright 2015 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" + +static void set_control_register( + unsigned mode, unsigned key_size, unsigned encrypt) +{ + GWRITE_FIELD(KEYMGR, AES_CTRL, RESET, CTRL_NO_SOFT_RESET); + GWRITE_FIELD(KEYMGR, AES_CTRL, KEYSIZE, key_size); + GWRITE_FIELD(KEYMGR, AES_CTRL, CIPHER_MODE, mode); + GWRITE_FIELD(KEYMGR, AES_CTRL, ENC_MODE, encrypt); + GWRITE_FIELD(KEYMGR, AES_CTRL, CTR_ENDIAN, CTRL_CTR_BIG_ENDIAN); + GWRITE_FIELD(KEYMGR, AES_CTRL, ENABLE, CTRL_ENABLE); + + /* Turn off random nops (which are enabled by default). */ + GWRITE_FIELD(KEYMGR, AES_RAND_STALL_CTL, STALL_EN, 0); + /* Configure random nop percentage at 25%. */ + GWRITE_FIELD(KEYMGR, AES_RAND_STALL_CTL, FREQ, 1); + /* Now turn on random nops. */ + GWRITE_FIELD(KEYMGR, AES_RAND_STALL_CTL, STALL_EN, 1); +} + +static int wait_read_data(volatile uint32_t *addr) +{ + int empty; + int count = 20; /* Wait these many ~cycles. */ + + do { + empty = REG32(addr); + count--; + } while (count && empty); + + return empty ? 0 : 1; +} + +int DCRYPTO_aes_init(const uint8_t *key, uint32_t key_len, const uint8_t *iv, + enum cipher_mode c_mode, enum encrypt_mode e_mode) +{ + int i; + const struct access_helper *p; + uint32_t key_mode; + + switch (key_len) { + case 128: + key_mode = 0; + break; + case 192: + key_mode = 1; + break; + case 256: + key_mode = 2; + break; + default: + /* Invalid key length specified. */ + return 0; + } + set_control_register(c_mode, key_mode, e_mode); + + /* Initialize hardware with AES key */ + p = (struct access_helper *) key; + for (i = 0; i < (key_len >> 5); i++) + GR_KEYMGR_AES_KEY(i) = p[i].udata; + /* Trigger key expansion. */ + GREG32(KEYMGR, AES_KEY_START) = 1; + + /* Wait for key expansion. */ + if (!wait_read_data(GREG32_ADDR(KEYMGR, AES_KEY_START))) { + /* Should not happen. */ + return 0; + } + + /* Initialize IV for modes that require it. */ + if (iv) { + p = (struct access_helper *) iv; + for (i = 0; i < 4; i++) + GR_KEYMGR_AES_CTR(i) = p[i].udata; + } + return 1; +} + +int DCRYPTO_aes_block(const uint8_t *in, uint8_t *out) +{ + int i; + struct access_helper *outw; + const struct access_helper *inw = (struct access_helper *) in; + + /* Write plaintext. */ + for (i = 0; i < 4; i++) + GREG32(KEYMGR, AES_WFIFO_DATA) = inw[i].udata; + + /* Wait for the result. */ + if (!wait_read_data(GREG32_ADDR(KEYMGR, AES_RFIFO_EMPTY))) { + /* Should not happen, ciphertext not ready. */ + return 0; + } + + /* Read ciphertext. */ + outw = (struct access_helper *) out; + for (i = 0; i < 4; i++) + outw[i].udata = GREG32(KEYMGR, AES_RFIFO_DATA); + return 1; +} + +void DCRYPTO_aes_write_iv(const uint8_t *iv) +{ + int i; + const struct access_helper *p = (const struct access_helper *) iv; + + for (i = 0; i < 4; i++) + GR_KEYMGR_AES_CTR(i) = p[i].udata; +} + +void DCRYPTO_aes_read_iv(uint8_t *iv) +{ + int i; + struct access_helper *p = (struct access_helper *) iv; + + for (i = 0; i < 4; i++) + p[i].udata = GR_KEYMGR_AES_CTR(i); +} + +int DCRYPTO_aes_ctr(uint8_t *out, const uint8_t *key, uint32_t key_bits, + const uint8_t *iv, const uint8_t *in, size_t in_len) +{ + /* Initialize AES hardware. */ + if (!DCRYPTO_aes_init(key, key_bits, iv, + CIPHER_MODE_CTR, ENCRYPT_MODE)) + return 0; + + while (in_len > 0) { + uint8_t tmpin[16]; + uint8_t tmpout[16]; + const uint8_t *inp; + uint8_t *outp; + const size_t count = MIN(in_len, 16); + + if (count < 16) { + memcpy(tmpin, in, count); + inp = tmpin; + outp = tmpout; + } else { + inp = in; + outp = out; + } + if (!DCRYPTO_aes_block(inp, outp)) + return 0; + if (outp != out) + memcpy(out, outp, count); + + in += count; + out += count; + in_len -= count; + } + return 1; +} |