You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[C] Software updates: size signature buffers from the request buffer - #398
AZ_IOT_SU_REQUEST_BUFFER_SIZE (4096) is smaller than a one-file offer (~4.8 KB).
verify_manifest_core() decoded into fixed stack buffers (header/sjwk 2048, sjwk payload 2048, sjwk header 512). A 2369-byte header overflowed and was logged as "not valid base64url".
Verification decodes into a caller-supplied scratch. The managed client passes its persistence scratch (free during verification). The SJWK signature, payload and signing key are decoded in place within the decoded outer header, so a signature that fits the request buffer verifies unless its SJWK header alone exceeds a quarter of it. Verifier stack 6544 -> 656 bytes (gcc -O2).
az_iot_su_parse_update_request() uses new AZ_IOT_SU_VERIFY_SCRATCH_SIZE (8192) bytes of stack.
A part that does not fit is logged as too large, with sizes; malformed input as not valid.
Base64 goes through new az_iot_base64_decode() (src/core/base64.c): validates alphabet, padding and zero trailing bits, then decodes with az_core in 64-character blocks, so in place is safe. azure-sdk-for-c left-shifts a negative value (UBSan) on out-of-alphabet input, e.g. a standard-base64 modulus given to the base64url decoder.
ESP32 sample: AZ_IOT_SU_REQUEST_BUFFER_SIZE=8192 for the IDF build; nvs partition 24 -> 36 KB so two generations of the largest checkpoint (8844 bytes) fit. OTA slots unchanged; existing devices need a flash erase to adopt the new table.
Docs: client-configuration, CHANGELOG, test coverage, ESP32 README.
Tests: large_signature_is_verified, near_limit_nested_signing_key_is_verified, oversized_signature_is_reported_as_too_large (all fail before this change); the first two assert the decoded key, exponent and signature. base64_test.c covers the helper. Partition tables checked with gen_esp32part.py; the ESP32 build was not run. Local: unit tests pass (gcc, ASan/UBSan clean), clang-format 18 and eng/check-*.sh clean.
- AZ_IOT_SU_REQUEST_BUFFER_SIZE defaults to 16384 (was 4096). A one-file
offer is about 5 KiB, so the former default refused it.
- Manifest verification decodes into a caller-supplied scratch instead of
fixed stack buffers (header and sjwk 2048, sjwk payload 2048, sjwk header
512). The managed client passes its persistence scratch, so any signature
that fits the request buffer decodes; the sjwk and modulus are unescaped in
place instead of copied. Verifier stack: 6544 -> 624 bytes.
- az_iot_su_parse_update_request() uses AZ_IOT_SU_VERIFY_SCRATCH_SIZE (8192)
bytes of stack.
- A part that does not fit is logged as too large, with its size, instead of
as invalid base64url.
- Base64 input is checked against its alphabet before decoding, avoiding a
negative left shift in azure-sdk-for-c on a standard-base64 modulus.
…eckpoints
- The SJWK signature and payload, and the signing key in it, are decoded in
place within the decoded outer header, with a decoder that is safe in
place. Only the SJWK header, the manifest signature or the JWS payload is
decoded beside the outer header, so a near-limit offer whose size is
mostly the signing key verifies in the client's scratch.
- ESP32 sample: AZ_IOT_SU_REQUEST_BUFFER_SIZE=8192 for the whole build and a
36 KB NVS partition, so two generations of the largest checkpoint (8844
bytes) fit while NVS replaces one with the other.
This test does not prove that the newly supported escaped standard-base64 modulus is decoded correctly: mock_verify_rs256() discards the modulus, exponent, and signature bytes, so any nonempty decoded output passes. Capture and assert the key bytes (or exercise a real verification backend), and add malformed/padding boundary cases for the new decoder; the C convention requires boundary and overflow coverage for new helpers.
…lper
- az_iot_base64_decode() (src/core/base64.c) validates the alphabet, padding
and zero trailing bits, then decodes through az_base64_url_decode() /
az_base64_decode() in 64-character blocks, so it may decode in place.
Replaces the local decoder in su_client.c.
- Tests: base64_test.c (tails, alphabets, malformed and non-canonical input,
bounds, block boundaries in place). The SU mock records the key, exponent
and signature passed to verify_rs256; the large-signature and near-limit
tests assert the decoded bytes.
The reason will be displayed to describe this comment to others. Learn more.
It does use azure-sdk-for-c: the decoding itself is az_base64_url_decode()/az_base64_decode(). The wrapper adds what those lack for untrusted signature input:
Validation first. On a character outside the alphabet, az_core left-shifts a negative value (UBSan: az_base64.c:249, hit by the standard-base64 modulus the service sends when given to the url decoder); it also accepts nonzero trailing bits (AB decodes like AA).
In-place decoding. Verification decodes the SJWK signature, payload and signing key in place within the decoded header so a near-limit offer fits the scratch; az_core does not document overlapping input/output, so the wrapper decodes in 64-character blocks through a small buffer.
One call that picks url or standard alphabet (the modulus may be either).
It replaced a local decoder in su_client.c after review asked for az_core plus a reusable, tested az_iot_* helper. No change made.
The reason will be displayed to describe this comment to others. Learn more.
It does use azure-sdk-for-c: the decoding itself is az_base64_url_decode()/az_base64_decode(). The wrapper adds what those lack for untrusted signature input:
Validation first. On a character outside the alphabet, az_core left-shifts a negative value (UBSan: az_base64.c:249, hit by the standard-base64 modulus the service sends when given to the url decoder); it also accepts nonzero trailing bits (AB decodes like AA).
In-place decoding. Verification decodes the SJWK signature, payload and signing key in place within the decoded header so a near-limit offer fits the scratch; az_core does not document overlapping input/output, so the wrapper decodes in 64-character blocks through a small buffer.
One call that picks url or standard alphabet (the modulus may be either).
It replaced a local decoder in su_client.c after review asked for az_core plus a reusable, tested az_iot_* helper. No change made.
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
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.
Software updates rejected current service offers:
AZ_IOT_SU_REQUEST_BUFFER_SIZE(4096) is smaller than a one-file offer (~4.8 KB).verify_manifest_core()decoded into fixed stack buffers (header/sjwk 2048, sjwk payload 2048, sjwk header 512). A 2369-byte header overflowed and was logged as "not valid base64url".Changes:
AZ_IOT_SU_REQUEST_BUFFER_SIZEdefault 16384.sizeof(az_iot_su_client)14216 -> 38792 bytes (x86-64);AZ_IOT_SU_STATE_BLOB_MAX_SIZE4748 -> 17036.az_iot_su_parse_update_request()uses newAZ_IOT_SU_VERIFY_SCRATCH_SIZE(8192) bytes of stack.az_iot_base64_decode()(src/core/base64.c): validates alphabet, padding and zero trailing bits, then decodes with az_core in 64-character blocks, so in place is safe. azure-sdk-for-c left-shifts a negative value (UBSan) on out-of-alphabet input, e.g. a standard-base64 modulus given to the base64url decoder.AZ_IOT_SU_REQUEST_BUFFER_SIZE=8192for the IDF build;nvspartition 24 -> 36 KB so two generations of the largest checkpoint (8844 bytes) fit. OTA slots unchanged; existing devices need a flash erase to adopt the new table.Tests:
large_signature_is_verified,near_limit_nested_signing_key_is_verified,oversized_signature_is_reported_as_too_large(all fail before this change); the first two assert the decoded key, exponent and signature.base64_test.ccovers the helper. Partition tables checked with gen_esp32part.py; the ESP32 build was not run. Local: unit tests pass (gcc, ASan/UBSan clean), clang-format 18 andeng/check-*.shclean.