fix(hotp): bound Non-Secure key size before copy into K[] - #254
Merged
Merged
Conversation
register_shared_key() copied params[0].memref.size bytes into the fixed 64-byte secure buffer K[] without bounding the length. That size is supplied verbatim by the Non-Secure caller, so any NS caller could overflow a secure .bss buffer with attacker-controlled data. This is not covered by the pointer-attribution hardening in 27e314c: nsec_check() proves the source range is Non-Secure resident, which is a different property from "the source fits in the destination". A legitimate 4 KB Non-Secure buffer passes that check exactly as designed and is then copied into 64 bytes. The correct bounds check already existed in this file (MIN_KEY_SIZE / MAX_KEY_SIZE in hmac_sha1()) but ran on the GET_HOTP path, a later and separate invocation, long after the corruption. Move the identical rejection ahead of the copy. Reject rather than truncate, matching apps/aes/ta/aes_ta.c: a silently shortened HMAC key would be a cryptographic defect of its own. Not a compatibility break, since such a key already failed later at hmac_sha1(). Two further defects in the same code: - DMSG traced K with "%s": it printed the HMAC shared secret to the console, and a key of exactly sizeof(K) leaves no NUL for %s to stop at, so it also over-read past the array. Trace only the length. - get_hotp() called truncate_() without checking res from hmac_sha1(), truncating uninitialised stack when the HMAC failed. Verified under QEMU (mps2-an505, cortex-m33): sizes 4096, 65 and 4 are now rejected with TEE_ERROR_BAD_PARAMETERS, the 20-byte RFC4226 key still registers and GET_HOTP still yields the specified value 755224. With this patch reverted the 4096-byte request returns TEE_SUCCESS and the secure world then stops responding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
register_shared_key()in the HOTP TA copiedparams[0].memref.sizebytes into thefixed 64-byte secure buffer
K[]without bounding the length. That size is suppliedverbatim by the non-secure caller, so any NS caller could overflow a secure
.bssbuffer with attacker-controlled data.
This is not covered by the pointer-attribution hardening merged in #250:
nsec_check()proves the source range is Non-Secure resident, which is a differentproperty from "the source fits in the destination". A legitimate 4 KB Non-Secure
buffer passes that check exactly as designed and is then copied into 64 bytes.
The right bounds check already existed in this file (
MIN_KEY_SIZE/MAX_KEY_SIZEinhmac_sha1()), but on theGET_HOTPpath — a later, separate invocation, long afterthe corruption. This moves the identical rejection to before the copy. Rejecting rather
than truncating matches
apps/aes/ta/aes_ta.c:251and avoids silently accepting ashortened HMAC key.
Two smaller defects in the same function are fixed alongside:
DMSGtracedKwith%s— it printed the HMAC shared secret to theconsole, and a key of exactly 64 bytes leaves no NUL for
%sto stop at, so it alsoover-read past the array. It now traces only the length.
get_hotp()calledtruncate_()without checkingresfromhmac_sha1(), so afailed HMAC truncated uninitialised stack.
Fixes #253
Type of change
Not a compatibility break: a key outside
MIN_KEY_SIZE..MAX_KEY_SIZEalready failed athmac_sha1()on the subsequentGET_HOTP, so no previously-working flow is rejected.How Has This Been Tested?
Reproducible QEMU harness, run in a container so the build is deterministic.
TA_HOTP_CMD_REGISTER_SHARED_KEYwith a validNon-Secure source buffer at sizes 4096, 65 (
sizeof(K)+1) and 4 (< MIN_KEY_SIZE).All three now return
0xFFFF0006 (TEE_ERROR_BAD_PARAMETERS).(
0x00000000) andGET_HOTPstill returns the specified vector value 755224.This also demonstrates the secure world survived the rejected attempts.
Control run with this patch reverted, same harness and same kernel: the 4096-byte
request returned
0x00000000(TEE_SUCCESS) and the secure world then stoppedproducing output — the remaining checks never executed. So the test genuinely detects
the defect rather than passing vacuously.
The temporary test app used for this is intentionally not included in this PR,
following the precedent of 102d3dc; it can be provided on request.
Test Configuration:
mps2-an505(Cortex-M33); build-only check onnumaker_pfm_m2351(Cortex-M23)Checklist: