| /* Copyright 2015 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" |
| |
| 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; |
| } |