Large key buffer allocated on stack in pk.c causes issues on constrained devices #476
Closed
opened 2025-09-24 23:08:04 -05:00 by Ashish285
·
10 comments
No Branch/Tag specified
development
tf-psa-crypto-1.1
archive/20261005T153337Z-force/pull/927
archive/20261001T141055Z-force/pull/927
archive/20261005T153337Z-force/pull/931
archive/20261001T083616Z-force/pull/927
archive/20260930T232327Z-force/pull/927
archive/20260930T154618Z-force/pull/927
archive/20260929T231919Z-rescue/rohankl-arm/mldsa-pubkey-899
archive/20260929T231919Z-rescue/sigvartmh/pr/spake2p-9342-public-key-import-export
archive/20260929T231919Z-rescue/ManoharPaturi/fix-pem-ct-padding
archive/20260929T231919Z-rescue/ManoharPaturi/fix-asn1write-null-guard
archive/20260929T231919Z-rescue/ManoharPaturi/fix-pbes2-params-validation
archive/20260929T231919Z-rescue/valeriosetti/blake2
archive/20260929T231919Z-rescue/jfdev001/v1.1.1_bump-cmake-minimum-version
archive/20260929T231919Z-rescue/jfdev001/bump-cmake-minimum-version
archive/20260929T231919Z-rescue/ronald-cron-arm/sm3-base
archive/20260929T231919Z-rescue/valeriosetti/blake2-hmac
archive/20260929T231919Z-rescue/v1rtu3x/enforce-hash-length-sign-verify
archive/20260929T231919Z-rescue/bjwtaylor/file-backed-keystore-doc
archive/20260929T231919Z-rescue/sigvartmh/pr/spake2p-9344-set-context
archive/20260929T231919Z-rescue/sigvartmh/pr/spake2p-9343-key-pair-import-export-depends-on-9342
archive/20260929T231919Z-rescue/romart/development
archive/20260929T231919Z-rescue/sweetlilmre/constant-time-embedded-perf
archive/20260929T231919Z-rescue/JulioLoayzaM/add-wycheproof-rsa-pss
archive/20260929T231919Z-rescue/JulioLoayzaM/add-wycheproof-ecdsa
archive/20260929T231919Z-rescue/JulioLoayzaM/add-wycheproof-tests
archive/20260929T231919Z-rescue/oubaid-a/dev_psa_driver_wrapper
archive/20260929T231919Z-rescue/gilles-peskine-arm/keystoreless-prototype-1
archive/20260929T231919Z-rescue/valeriosetti/issue9580
archive/20260929T231919Z-rescue/LeonieReichertABB/hpke-functionality
archive/20260929T231919Z-rescue/gilles-peskine-arm/psa-hash-no-buffer-copy-1.1
archive/20260929T231919Z-rescue/vanhauser-thc/development
archive/20260929T231919Z-rescue/tpambor/key-wrap
archive/20260929T231919Z-rescue/ruiliio/psa_xts
archive/20261011T063515Z-force/pull/538
archive/20260929T231919Z-rescue/gilles-peskine-arm/fix-broken-links-1.1
archive/20260929T231919Z-rescue/valeriosetti/issue669
archive/20260929T231919Z-rescue/gilles-peskine-arm/prepare-release-test
archive/20260929T231919Z-rescue/krish2718/wpa3-sae-design-note
archive/20260929T231919Z-rescue/nzzlinh/fix_iar_endianness_1.1
archive/20260929T231919Z-rescue/nzzlinh/fix-iar-endianness
archive/20260929T231919Z-rescue/abtom87/fix-pbkdf2-iter-count
archive/20260929T231919Z-rescue/minosgalanakis/tools/prepare_release_lite
archive/20260929T231919Z-rescue/Harshal5/fix/aes-xts-in-place-cts
archive/20260929T231919Z-rescue/sigvartmh/pr/spake2p-9347-prover-depends-on-9342-9343-9344
archive/20260929T231919Z-rescue/crabel99/async-hardware-support-c35df962
tf-psa-crypto-1.2.0_mergeback
tf-psa-crypto-1.1.1_mergeback
archive/20260929T231919Z-rescue/asmellby/feature/dispatch-library
tf-psa-crypto-release-sync
archive/20260929T231919Z-rescue/ChakshuGupta13/fix/ecdsa-zeroize-stack-key
archive/20260929T231919Z-rescue/gilles-peskine-arm/security-md-option-names
archive/20260929T231919Z-rescue/parmi93/patch-2
archive/20260929T231919Z-rescue/bjwtaylor/remove-mldsa-sign-from-build
archive/20260929T231919Z-rescue/bjwtaylor/tf-m-build-issues
archive/20260929T231919Z-rescue/bjwtaylor/test-context-population
archive/20260929T231919Z-rescue/gilles-peskine-arm/iar-1.1.0
archive/20260929T231919Z-rescue/gilles-peskine-arm/fork-helper-unit-tests-create-crypto
archive/20260929T231919Z-rescue/bjwtaylor/redundant-declarations
archive/20260929T231919Z-rescue/hasheddan/docs/fix-broken-links
main
archive/20260929T231919Z-rescue/bjwtaylor/reintroduce-dh
archive/20260929T231919Z-rescue/bjwtaylor/cipher-update-incorrect-error
archive/20260929T231919Z-rescue/Marandil/fix/mbedtls-4314-parse-explicit-curve
archive/20260929T231919Z-rescue/schneider-electric/dev/plaj-se/parse-rsassa-pss
archive/20260929T231919Z-rescue/minosgalanakis/bugfix/program-fuzz-mscv
archive/20260929T231919Z-rescue/daverodgman/aesgcm
archive/20260929T231919Z-rescue/gilles-peskine-arm/sha3-coverity-20260224
archive/20260929T231919Z-rescue/bensze01/undef
archive/20260929T231919Z-rescue/valeriosetti/issue523
archive/20260929T231919Z-rescue/dannytsen/ppc-dev
archive/20260929T231919Z-rescue/daverodgman/aes12
archive/20260929T231919Z-rescue/gilles-peskine-arm/ascon-prototype
archive/20260929T231919Z-rescue/daverodgman/chacha10
archive/20260929T231919Z-rescue/kusumitg/implement_psa_pake_set_context_for_spake2p
archive/20260929T231919Z-rescue/davidhorstmann-arm/add-code-size-target
archive/20260929T231919Z-rescue/bjwtaylor/simplify-sign_func
archive/20260929T231919Z-rescue/mfil/feature/aes_ni_parallel
archive/20260929T231919Z-rescue/ivv19041994/patch-1
archive/20260929T231919Z-rescue/gilles-peskine-arm/psa-verify-policy-relax
public_2/main
archive/20260929T231919Z-rescue/mpg/pk-sign-proto
release/tf-psa-crypto1.0.0-documentation
archive/20260929T231919Z-rescue/gilles-peskine-arm/0e-plans-july-updates
archive/20260929T231919Z-rescue/davidhorstmann-arm/guard-context-types-in-client-only
archive/20260929T231919Z-rescue/gilles-peskine-arm/psa-driver-interface-spec-remove
archive/20260929T231919Z-rescue/gilles-peskine-arm/ecp-self-test-race-crypto
archive/20260929T231919Z-rescue/KiruthikPrakash/feature/9342_add-support-for-spake2p-public-key-import-export
archive/20260929T231919Z-rescue/ruiliio/chacha20_16
archive/20260929T231919Z-rescue/irwir/winsock2
archive/20260929T231919Z-rescue/aphroteus/development
archive/20260929T231919Z-rescue/amjoul01/amjoul01/psa-mac-out-of-bound-fix
archive/20260929T231919Z-rescue/gilles-peskine-arm/everest-submodule-crypto
archive/20260929T231919Z-rescue/yamt/xtensa-ccount
archive/20260929T231919Z-rescue/gilles-peskine-arm/all.sh-generate-only-before-checks-crypto
archive/20260929T231919Z-rescue/Harshal5/feat/builtin_cmac_driver_use_psa_crypto_interface
archive/20260929T231919Z-rescue/gilles-peskine-arm/test-data_t-empty_is_empty-crypto
archive/20260929T231919Z-rescue/jean-baptisteboric-eaton/oids-pkcs7-authenticated-attributes
dev/bensze01/public-only-commit
archive/20260929T231919Z-rescue/waleed-elmelegy-arm/psa-key-wrap-aes-kw
archive/20260929T231919Z-rescue/tfloch/psa_add_xts_algorithm
archive/20260929T231919Z-rescue/gilles-peskine-arm/msancf-clang16-crypto
archive/20260929T231919Z-rescue/DemiMarie/document-unexpected-tags
archive/20260929T231919Z-rescue/DemiMarie/strict-len
archive/20260929T231919Z-rescue/valeriosetti/issue9255-tfpsacrypto
archive/20260929T231919Z-rescue/daantimmer/use-compiler-frontend
archive/20260929T231919Z-rescue/benmcollins/enable-rsassa-pss-pkparse
dev/paul-elliott-arm/add_iop_key_agreement_negative_tests
add-iop-export-pub-key-get-num-ops
dev/rc/new-2-d635ea1
dev/bensze01/jenkins
v1.2.0
tf-psa-crypto-1.2.0
v1.1.1
tf-psa-crypto-1.1.1
v1.1.0
tf-psa-crypto-1.1.0
v1.0.0
tf-psa-crypto-1.0.0
v1.0.0-beta
tf-psa-crypto-1.0.0-beta
Labels
Clear labels
api-break
This issue/PR breaks the API and must wait for a new major version
approved
Design and code approved - may be waiting for CI or backports
bug
Something isn't working
component-docs
Docs / website issues filed here for tracking
component-platform
Portability layer and build scripts
component-test
Test framework and CI scripts
dependencies
Pull requests that update a dependency file
deprecation
needs deprecation announcement
DO-NOT-MERGE
enhancement
New feature or request
good first issue
Good for newcomers
help wanted
This issue is not being actively worked on, but PRs welcome.
needs-adoption
stalled PR that someone should pick up and complete
needs-backports
Backports are missing or are pending review and approval.
needs-ci
Needs to pass CI tests
needs-design-approval
Needs design discussion / approval
needs-info
An issue or PR which needs further info from the reporter / author
needs-preceding-pr
Requires another PR to be merged first
needs-review
Every commit must be reviewed by at least two team members
needs-reviewer
This PR needs someone to pick it up for review
needs-work
priority-high
High priority - will be reviewed soon
priority-low
Low priority - this may not receive review soon
priority-medium
Medium priority - this can be reviewed as time permits
priority-scheduled
This PR is big - it will require time to be scheduled for review
priority-very-high
Highest priority - prioritise this over other review work
resolved-after-1.1
The issue is resolved in development, but still present in 1.1 LTS.
single-reviewer
This PR qualifies for having only one reviewer
size-l
Estimated task size: large (2w+)
size-m
Estimated task size: medium (~1w)
size-optimisation
size-s
Estimated task size: small (~2d)
size-xs
Estimated task size: extra small (a few hours at most)
No labels
api-break
approved
bug
component-docs
component-platform
component-test
dependencies
deprecation
DO-NOT-MERGE
enhancement
good first issue
help wanted
needs-adoption
needs-backports
needs-ci
needs-design-approval
needs-info
needs-preceding-pr
needs-review
needs-reviewer
needs-work
priority-high
priority-low
priority-medium
priority-scheduled
priority-very-high
resolved-after-1.1
single-reviewer
size-l
size-m
size-optimisation
size-s
size-xs
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
gh-Mbed-TLS/TF-PSA-Crypto#476
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Description
In drivers/builtin/src/pk.c#L727, a key buffer is allocated directly on the stack.
On edge devices with limited stack memory, this can cause stack overflows or severely limit available stack space for other operations.
Suggested enhancement
Hi @Ashish285 and thanks for your report!
You're making a good point. When we added this buffer it was only used for ECC keys, which are always relatively small, so I think a stack buffer was appropriate at that time. Later we started using it for RSA keys as well, making the maximum size much larger, and we didn't revisit the location of this buffer.
I think that's probably our best option. PK already relies on heap allocations quite heavily - until 3.6 it directly made allocations, and even now it indirectly makes a lot of them via bignum under the hood. So I don't think it makes sense to avoid a heap allocation here.
I don't think a fixed buffer is required. However, regarding compile-time configuration, note that if your application is only using ECC keys, you'll want to make sure support for RSA keys (
PSA_WANT_KEY_TYPE_RSA_PUBLIC) is disabled in your config. This should make the size of this buffer more reasonable.The library has users with no heap, and users with a small stack. It's hard to satisfy both...
We have a compile-time option
MBEDTLS_PSA_STATIC_KEY_SLOTSwhich removes all uses ofmalloc()in the PSA key store (at the expense of more global memory consumption and a few limitations). We could generalize this option to mean “don't usemalloc()in places where it's avoidable”, so that could affect PK too.Not when you only use ECC and all the crypto is done by a driver, which was the practical scenario that our keenest heapless user got to work.
We're overdue for adding PQC support. It's likely that PK will start supporting ML-DSA in 2026, and you really don't want an ML-DSA public key on the stack. Or even just to consume the size of the largest ML-DSA key in all PK objects even when you happen to be doing ECC. So that will be another reason to make the buffer heap-allocated, at least in most configurations.
I was on the fence treating that as a bug or even regression from 3.6 to 1.0: some use cases worked in 3.6 and no longer work in 1.0 (and that was not intentional, unlike all the removals).
Sharing some thoughts I had about this.
One approach could be to stop storing public keys serialized in the PK context, but instead keep them in the PSA key store like we do for private keys. The primary motivation for not using the key store was that is used to not be dynamic and some use cases (loading 100+ X.509 trusted roots) would make it full - that's no longer an issue now. However there was a secondary motivation which is that a serialized key has no policy, which allows using whatever algorithm we want with the public key. If we were storing public keys in the PSA key store, we'd has to be prepared to export and re-import them with a different alg in some cases, which is only moving the problem of the buffer to a different place.
Another approach we could take is to keep storing public keys serialized in the PK context, but make the buffer static only when ECC is the only key type, and dynamic otherwise. Something like
I think this could give users the best of both worlds at a reasonable cost.
Thoughts?
As discussed elsewhere, we're handling this as a bug, but not a severe one. We'll try to fix in 1.1 but are not making promises.
As was pointed out in private discussions, there are multiple buffers being discussed here:
PSA_KEY_EXPORT_MAX_SIZE-sized buffer inexport_import_into_psa()(permanent link, used by the public functionmbedtls_pk_import_into_psa().MBEDTLS_PK_MAX_PUBKEY_RAW_LEN-size buffer in the structurembedtls_pk_context(permanent link) so my comments were about that one.Sorry for the confusion.
Moving forward, I'm trying to get a complete list of large stack buffers we a have in PK. I used two complementary approaches, comparing their results. The first relies on GCC's
-fstack-sizeoption:The second approach is based on looking for patterns in the source code:
The results are quite consistent. The source-based approach finds a few buffers that are smaller than the threeshold used of the compiler-based approach, and also finds the buffer in the structure (the one I had in mind). The compiler-based approach identifies
pk_use_ecparams()as using a lot of stack space, but looking at the source code, that doesn't seem to be related to a large buffer for key material, so I'm excluding this function from further analysis (also, I strongly suspect this function stack usage can be brought down by disablingMBEDTLS_PK_PARSE_EC_EXTENDED).Here's a more detailed analysis of the large buffers for key material identified by the source-based approach, ranked from largest to smallest. (For reference, line numbers are in today's development, which is
29cc9f70b8.)2363 all-types keypair
The one originally reported.
2363 all-types keypair or public
Very similar to the previous one.
2363 RSA keypair
Note: could write directly to the output buffer I think. Edit: being done in https://github.com/Mbed-TLS/TF-PSA-Crypto/pull/684
528
1024all-types public(wrongly includes FFDH, should be 528)Note: useless now, could directly access
pk->pub_rawinstead, see https://github.com/Mbed-TLS/mbedtls/issues/10583. Edit: in the meantime, size brought down to 528 by #679133
1024EC public(wrongly includes RSA and FFDH, should be 133)Note: useless now, could directly access
pk->pub_rawinstead, see https://github.com/Mbed-TLS/mbedtls/issues/10583.Edit: in the meantime, size brought down to 133 by #679
528 all-types public
Structure, used in a number of places. (The one I had in mind.)
528 RSA publicNote: being removed by #580 - edit: merged, so this buffer is now gone.
528 RSA public
Note: useless now, could directly access
pk->pub_rawinstead, see https://github.com/Mbed-TLS/mbedtls/issues/10583.133 ECC public
Note: useless now, could directly access
pk->pub_rawinstead, see https://github.com/Mbed-TLS/mbedtls/issues/10583.66 EC keypair
Note: could possibly write directly to the output buffer?
Clearly the last two are harmless and will be excluded from further analysis.
Just looking at the sizes, there is a large gap between the first three (2368 bytes, able to hold an RSA keypair) and the next ones, which should be 528 bytes (able to hold an RSA public key (or an ECC public key or keypair)) if we hadn't made silly mistakes while defining our macros.
Among the first three (2368 bytes), I'd say the first two are more worrying than the third one: that's because they must cover all types supported by PK, which means their size will grow when we add support for ML-DSA (whose key sizes go up to 4896 for private keys, 2592 for public keys). By contrast, the 3rd one is purely about RSA, which means it isn't growing when adding support for ML-DSA (or other PQ schemes), and the price is only paid when you actually use RSA keys.
Then in the 528-bytes group (assuming we fix the silly mistakes in macro definitions), we can make the same distinction. Further, we can disregard those that could just directly access
pub_rawinstead - which quite logically would leave us with thepub_rawfield in thembedtls_pk_contextstructure as the only one in that category.I'll think about possible solutions and come back later.
Most of the large stack buffers identified are now being addressed by other PRs or other issues - I've edited the previous comment with links. The only remaining ones are:
mbedtls_pk_import_into_psa(),mbedtls_pk_write_pubkey(),mbedtls_pk_write_key_der()which are very similar in nature.mbedtls_pk_contextstructure.While (2) will be problematic in the future (when we add support for ML-DSA with its large public keys), I suggest we consider it out of scope for the present issue, and consider it resolved when the last 2k+ stack buffers are removed.
I found it easier to express the proposal as a patch than in English, so here it is:
#684edit: #687So, my proposal preserves the fact that we're not using the heap when ECC is the only key type enabled in PK. We only use the heap when RSA keys are enabled (and later ML-DSA of course). We never officially supported driver-only RSA (though it should mostly work by now) so I don't think using the heap for RSA in PK is a regression.
@gilles-peskine-arm I'd appreciate if you could take a look at the design of
#684edit: #687.