Skip to content

Commit

Permalink
[ot] hw/opentitan: ot_hmac: Add HMAC SHA-2 384/512 & key size support
Browse files Browse the repository at this point in the history
This commit introduces full support for using the SHA-2 384 and SHA-2
512 algorithms for hashing alongside the existing SHA-2 256 algorithm,
by setting the `digest_size` field in the configuration register. It
likewise adds support for using a range of key sizes (128/256/384/512/
1024), and not just always using a 512 bit key - this is set in the
`key_length` field in the configuration register.

The existing tomcrypt library is used to support the additional hash
functionality, which uses `sha512` functionality for both sha384 and
sha512 in its implementation.

This commit also fixes an issue previously introduced where two of
the `bswap`s on the digest were the wrong way around, cancelling
each other out but technically producing an incorrect intermediary
result. This fixes that behaviour.
  • Loading branch information
AlexJones0 committed Dec 16, 2024
1 parent 8cd8180 commit 749db49
Showing 1 changed file with 145 additions and 24 deletions.
169 changes: 145 additions & 24 deletions hw/opentitan/ot_hmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,16 +413,105 @@ static void ot_hmac_report_error(OtHMACState *s, uint32_t error)
static void ot_hmac_writeback_digest_state(OtHMACState *s)
{
/* copy intermediary digest to mock HMAC's stop/continue behaviour. */
/* TODO: add support for SHA2-384 and SHA2-512 */
for (unsigned i = 0; i < 8u; i++) {
STORE32H(s->ctx->state.sha256.state[i], s->regs->digest + i);
switch (ot_hmac_get_digest_size(s->regs->cfg)) {
case HMAC_SHA2_256:
for (unsigned idx = 0; idx < 8u; idx++) {
STORE32H(s->ctx->state.sha256.state[idx], s->regs->digest + idx);
}
break;
case HMAC_SHA2_384:
/*
* Even though SHA384 only uses the first six uint64_t values of
* the SHA512 digest, we must store all for intermediary computation.
*/
case HMAC_SHA2_512:
for (unsigned idx = 0; idx < 8u; idx++) {
STORE64H(s->ctx->state.sha512.state[idx],
s->regs->digest + 2 * idx);
}
break;
case HMAC_SHA2_NONE:
default:
/*
* Should never happen: digest size was validated when calling start /
* continue to begin operation.
*/
g_assert_not_reached();
}
}

static void ot_hmac_restore_context(OtHMACState *s)
{
switch (ot_hmac_get_digest_size(s->regs->cfg)) {
case HMAC_SHA2_256:
s->ctx->state.sha256.curlen = 0;
s->ctx->state.sha256.length = s->regs->msg_length;
for (unsigned idx = 0; idx < 8u; idx++) {
LOAD32H(s->ctx->state.sha256.state[idx], s->regs->digest + idx);
}
break;
case HMAC_SHA2_384:
/*
* Even though SHA384 only uses the first six uint64_t values of
* the SHA512 digest, we must restore all for intermediary computation.
*/
case HMAC_SHA2_512:
s->ctx->state.sha512.curlen = 0;
s->ctx->state.sha512.length = s->regs->msg_length;
for (unsigned idx = 0; idx < 8u; idx++) {
LOAD64H(s->ctx->state.sha512.state[idx], s->regs->digest + 2 * idx);
}
break;
case HMAC_SHA2_NONE:
default:
/*
* Should never happen: digest size was validated when receiving the
* continue command to (re-)begin operation.
*/
g_assert_not_reached();
}
}

static size_t ot_hmac_get_curlen(OtHMACState *s)
{
switch (ot_hmac_get_digest_size(s->regs->cfg)) {
case HMAC_SHA2_256:
return s->ctx->state.sha256.curlen;
case HMAC_SHA2_384:
case HMAC_SHA2_512:
return s->ctx->state.sha512.curlen;
case HMAC_SHA2_NONE:
default:
/*
* Should never happen: digest size was validated when calling start /
* continue to begin operation.
*/
g_assert_not_reached();
return 0u;
}
}

static void ot_hmac_sha_init(OtHMACState *s, bool write_back)
{
/* TODO: add support for SHA2-384 and SHA2-512 */
sha256_init(&s->ctx->state);
switch (ot_hmac_get_digest_size(s->regs->cfg)) {
case HMAC_SHA2_256:
sha256_init(&s->ctx->state);
break;
case HMAC_SHA2_384:
sha384_init(&s->ctx->state);
break;
case HMAC_SHA2_512:
sha512_init(&s->ctx->state);
break;
case HMAC_SHA2_NONE:
default:
/*
* Should never happen: digest size was validated when calling start /
* continue to begin operation.
*/
g_assert_not_reached();
return;
}
if (write_back) {
ot_hmac_writeback_digest_state(s);
}
Expand All @@ -431,17 +520,52 @@ static void ot_hmac_sha_init(OtHMACState *s, bool write_back)
static void ot_hmac_sha_process(OtHMACState *s, const uint8_t *in, size_t inlen,
bool write_back)
{
/* TODO: add support for SHA2-384 and SHA2-512 */
sha256_process(&s->ctx->state, in, inlen);
switch (ot_hmac_get_digest_size(s->regs->cfg)) {
case HMAC_SHA2_256:
sha256_process(&s->ctx->state, in, inlen);
break;
/* NOLINTNEXTLINE */
case HMAC_SHA2_384:
sha384_process(&s->ctx->state, in, inlen);
break;
case HMAC_SHA2_512:
sha512_process(&s->ctx->state, in, inlen);
break;
case HMAC_SHA2_NONE:
default:
/*
* Should never happen: digest size was validated when calling start /
* continue to begin operation.
*/
g_assert_not_reached();
return;
}
if (write_back) {
ot_hmac_writeback_digest_state(s);
}
}

static void ot_hmac_sha_done(OtHMACState *s)
{
/* TODO: add support for SHA2-384 and SHA2-512 */
sha256_done(&s->ctx->state, (uint8_t *)s->regs->digest);
switch (ot_hmac_get_digest_size(s->regs->cfg)) {
case HMAC_SHA2_256:
sha256_done(&s->ctx->state, (uint8_t *)s->regs->digest);
return;
case HMAC_SHA2_384:
sha384_done(&s->ctx->state, (uint8_t *)s->regs->digest);
return;
case HMAC_SHA2_512:
sha512_done(&s->ctx->state, (uint8_t *)s->regs->digest);
return;
case HMAC_SHA2_NONE:
default:
/*
* Should never happen: digest size was validated when calling start /
* continue to begin operation.
*/
g_assert_not_reached();
return;
}
}

static void ot_hmac_compute_digest(OtHMACState *s)
Expand Down Expand Up @@ -478,9 +602,9 @@ static void ot_hmac_process_fifo(OtHMACState *s)
bool stop = s->regs->cmd & R_CMD_HASH_STOP_MASK;

if (!fifo8_is_empty(&s->input_fifo) &&
(!stop || s->ctx->state.sha256.curlen != 0)) {
(!stop || ot_hmac_get_curlen(s) != 0)) {
while (!fifo8_is_empty(&s->input_fifo) &&
(!stop || s->ctx->state.sha256.curlen != 0)) {
(!stop || ot_hmac_get_curlen(s) != 0)) {
uint8_t value = fifo8_pop(&s->input_fifo);
ot_hmac_sha_process(s, &value, 1u, false);
}
Expand All @@ -496,7 +620,7 @@ static void ot_hmac_process_fifo(OtHMACState *s)
}
}

if (stop && s->ctx->state.sha256.curlen == 0) {
if (stop && ot_hmac_get_curlen(s) == 0) {
s->regs->intr_state |= INTR_HMAC_DONE_MASK;
s->regs->cmd = 0;
}
Expand Down Expand Up @@ -641,8 +765,9 @@ static uint64_t ot_hmac_regs_read(void *opaque, hwaddr addr, unsigned size)
case R_DIGEST_14:
case R_DIGEST_15:
/*
* We use a sha library in little endian by default, so we only need to
* swap if the swap config is 1 (big endian digest).
* The sha library we use expects a big endian digest for its
* intermediate computations, so we only need to swap if the config is
* 0 (little endian digest).
*/
if (s->regs->cfg & R_CFG_DIGEST_SWAP_MASK) {
val32 = s->regs->digest[reg - R_DIGEST_0];
Expand Down Expand Up @@ -875,12 +1000,7 @@ static void ot_hmac_regs_write(void *opaque, hwaddr addr, uint64_t value,

s->regs->cmd = R_CMD_HASH_CONTINUE_MASK;

/* Restore SHA256 context */
s->ctx->state.sha256.curlen = 0;
s->ctx->state.sha256.length = s->regs->msg_length;
for (unsigned i = 0; i < 8u; i++) {
s->ctx->state.sha256.state[i] = s->regs->digest[i];
}
ot_hmac_restore_context(s);

/* trigger delayed processing of FIFO */
ibex_irq_set(&s->clkmgr, true);
Expand Down Expand Up @@ -978,13 +1098,14 @@ static void ot_hmac_regs_write(void *opaque, hwaddr addr, uint64_t value,
}

/*
* We use a sha library in little endian by default, so we only need to
* swap if the swap config is 1 (big endian digest).
* The sha library we use expects a big endian digest for its
* intermediate computations, so we only need to swap if the config is
* 0 (little endian digest).
*/
if (s->regs->cfg & R_CFG_DIGEST_SWAP_MASK) {
s->regs->digest[reg - R_DIGEST_0] = bswap32(val32);
} else {
s->regs->digest[reg - R_DIGEST_0] = val32;
} else {
s->regs->digest[reg - R_DIGEST_0] = bswap32(val32);
}
break;
case R_MSG_LENGTH_LOWER:
Expand Down

0 comments on commit 749db49

Please sign in to comment.