lms: zeroize create_merkle_internal_value output on failure, alias-safely

Follow-up to #10976. The entry-condition memset() used by the other LMS
and LMOTS helpers cannot be applied to create_merkle_internal_value():
mbedtls_lms_verify() passes Tc_candidate_root_node as out while it is
also left_node or right_node, so wiping out on entry destroys an input
before psa_hash_update() reads it (as 0xiviel found on #10976).

Instead, hash into a local buffer and write out exactly once, at exit,
after both inputs have been consumed: the digest on success, zeros on
every failure path. The local buffer is zeroized before returning. This
gives create_merkle_internal_value() the same "out never holds stale
data on failure" property as its siblings, without depending on out
being distinct from the inputs.

psa_hash_finish() is still given MBEDTLS_LMS_M_NODE_BYTES(params->type)
as the output size, unchanged from before.

Verified (MBEDTLS_LMS_PRIVATE set): tests/test_suite_lms PASSED
(25 / 25) and tests/test_suite_lmots PASSED (17 / 17, 1 skipped), both
with a plain build and with -fsanitize=address,undefined, no
diagnostics.

Signed-off-by: Johnson Ajibi <ajibijohnson@gmail.com>
This commit is contained in:
Johnson Ajibi 2026-10-21 21:47:18 +01:00
parent 1ee94c5a11
commit 968cc72440

View File

@ -177,6 +177,10 @@ static int create_merkle_internal_value(const mbedtls_lms_parameters_t *params,
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t output_hash_len;
unsigned char r_node_idx_bytes[4];
/* out may alias left_node or right_node (mbedtls_lms_verify() walks the
* path in place), so hash into a local buffer and only write out at exit,
* after both inputs have been consumed. */
unsigned char node[MBEDTLS_LMS_M_NODE_BYTES_MAX];
op = psa_hash_operation_init();
status = psa_hash_setup(&op, PSA_ALG_SHA_256);
@ -213,7 +217,7 @@ static int create_merkle_internal_value(const mbedtls_lms_parameters_t *params,
goto exit;
}
status = psa_hash_finish(&op, out, MBEDTLS_LMS_M_NODE_BYTES(params->type),
status = psa_hash_finish(&op, node, MBEDTLS_LMS_M_NODE_BYTES(params->type),
&output_hash_len);
if (status != PSA_SUCCESS) {
goto exit;
@ -222,6 +226,13 @@ static int create_merkle_internal_value(const mbedtls_lms_parameters_t *params,
exit:
psa_hash_abort(&op);
if (status == PSA_SUCCESS) {
memcpy(out, node, MBEDTLS_LMS_M_NODE_BYTES(params->type));
} else {
memset(out, 0, MBEDTLS_LMS_M_NODE_BYTES(params->type));
}
mbedtls_platform_zeroize(node, sizeof(node));
return PSA_TO_MBEDTLS_ERR(status);
}