The AST2600, AST1030 and later crypto engines add AES/DES/3DES CTR mode
(HACE10[6:4] = 0b100) on top of the ECB/CBC modes shared with the
AST2500. Decode the CTR selection, round the working buffers up to a
whole block so the stream-like final block is still processed a block at
a time, and write the counter advanced by the number of blocks consumed
back to the context buffer so the driver can continue across requests.
Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
hw/misc/aspeed_hace.c | 50 ++++++++++++++++++++++++++++++++++++++-----
1 file changed, 45 insertions(+), 5 deletions(-)
diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c
index 59eb1aeae3..b7a7c0b7ff 100644
--- a/hw/misc/aspeed_hace.c
+++ b/hw/misc/aspeed_hace.c
@@ -42,6 +42,7 @@
#define CRYPT_CMD_OP_MODE_MASK (0x7 << 4)
#define CRYPT_CMD_ECB (0x0 << 4)
#define CRYPT_CMD_CBC (0x1 << 4)
+#define CRYPT_CMD_CTR (0x4 << 4)
/* AES key length HACE10[3:2] */
#define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2)
#define CRYPT_CMD_AES256 (0x2 << 2)
@@ -589,6 +590,9 @@ static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg,
case CRYPT_CMD_CBC:
*mode = QCRYPTO_CIPHER_MODE_CBC;
break;
+ case CRYPT_CMD_CTR:
+ *mode = QCRYPTO_CIPHER_MODE_CTR;
+ break;
default:
return false;
}
@@ -652,6 +656,22 @@ static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr,
return copied == len;
}
+/*
+ * Add @add to the big-endian counter block @ctr (@len bytes) in place, so the
+ * CTR mode counter can be advanced by the number of blocks just consumed.
+ */
+static void crypt_be_add(uint8_t *ctr, size_t len, uint64_t add)
+{
+ size_t i = len;
+
+ while (i > 0 && add) {
+ i--;
+ add += ctr[i];
+ ctr[i] = add & 0xff;
+ add >>= 8;
+ }
+}
+
/*
* Perform an AES/DES/3DES ECB/CBC operation. The source and destination are
* either single contiguous buffers (direct access mode) or scatter-gather
@@ -677,6 +697,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
uint64_t dst_addr;
size_t iv_offset;
size_t blocklen;
+ size_t buf_len;
size_t keylen;
bool status;
@@ -722,8 +743,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
return;
}
- src_buf = g_malloc0(len);
- dst_buf = g_malloc0(len);
+ /*
+ * Round the working buffers up to a whole block. Block modes are already
+ * block-aligned; the stream-like CTR mode may leave a partial final block
+ * that the engine still processes a full block at a time.
+ */
+ buf_len = QEMU_ALIGN_UP(len, blocklen);
+ src_buf = g_malloc0(buf_len);
+ dst_buf = g_malloc0(buf_len);
/* Gather the source into the bounce buffer, per the selected mode. */
src_addr = s->regs[R_CRYPT_SRC];
@@ -744,7 +771,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
}
if (encrypt) {
- if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len,
+ if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, buf_len,
&local_err) < 0) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n",
__func__, error_get_pretty(local_err));
@@ -752,7 +779,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
return;
}
} else {
- if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len,
+ if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, buf_len,
&local_err) < 0) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n",
__func__, error_get_pretty(local_err));
@@ -785,13 +812,26 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd)
* output when encrypting, or of the input when decrypting. Write it
* back as the IV for the next request.
*/
- next_iv = (encrypt ? dst_buf : src_buf) + len - blocklen;
+ next_iv = (encrypt ? dst_buf : src_buf) + buf_len - blocklen;
if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
__func__, ctx_addr + iv_offset);
}
+ } else if (mode == QCRYPTO_CIPHER_MODE_CTR) {
+ /*
+ * CTR chains on the counter, which advances by one per block. Add the
+ * number of blocks processed (buf_len / blocklen) and write it back.
+ */
+ crypt_be_add(ctx + iv_offset, blocklen, buf_len / blocklen);
+ if (address_space_write(&s->dram_as, ctx_addr + iv_offset,
+ MEMTXATTRS_UNSPECIFIED, ctx + iv_offset,
+ blocklen)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n",
+ __func__, ctx_addr + iv_offset);
+ }
}
}
--
2.43.0