psa_cipher_update reports BUFFER_TOO_SMALL even if buffer size equals to PSA_CIPHER_UPDATE_OUTPUT_SIZE #592
Open
opened 2025-12-07 11:31:31 -06:00 by jnmeurisse
·
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#592
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?
Summary
psa_cipher_update returns BUFFER_TOO_SMALL status even if buffer is allocated with size equals to PSA_CIPHER_UPDATE_OUTPUT_SIZE. This error is reported when decrypting a buffer using PSA_ALG_CBC_PKCS7 algorithm.
System information
TF-PSA-Crypto version (number or commit id): 1.0.0
Operating system and version: Linux 5.15.0-161-generic
Configuration (if not default, please attach
crypto_config.h): default crypto_config.hExpected behavior
psa_cipher_update does not fail when we provide an output buffer sized with PSA_CIPHER_UPDATE_OUTPUT_SIZE.
Actual behavior
psa_cipher_update returns BUFFER_TOO_SMALL when psa_cipher_update is called more than once. In fact psa_cipher_update fails when the total input length is larger than the block size.
Steps to reproduce
Hi @jnmeurisse, thank you for your interest in TF-PSA-Crypto and for your bug report. I was trying to access the reproducer at https://github.com/user-attachments/files/24017405/test_psa.c, but I am getting a 404. Do you think you could paste it in a message or attach it in a different way?
CBC is based on blocks, so each update call may output a little less or a little more than the input, depending on the block alignment. CBC-PKCS7 decryption requires an output buffer that's larger than the actual data in some cases, so that its execution time doesn't depend on the amount of padding. But I thought that
PSA_CIPHER_UPDATE_OUTPUT_SIZEandPSA_CIPHER_FINISH_OUTPUT_SIZEalready accounted for these peculiarities. I guess we didn't get it right? But we would definitely need to see a reproducer.Here is the source code demonstrating the issue:
The test code passes the variable
to_processasoutput_sizeto psa_cipher_update(). As far as I can tellto_processis not guaranteed to be appropriate as required my documentation. As far as I can tell, the library is working as intended.It is a typo in my test program, sorry.
If you pass output_buffer_size instead of to_process as effective argument (lines 65 and 165), psa_cipher_update still reports error -138.
Example scenario
Plaintext size: 16 bytes.
Encryption
The Encryption produces:
psa_cipher_update→ 16 bytespsa_cipher_finish→ 16 bytesTotal ciphertext = 32 bytes.
Decryption
During decryption, processing the 32-byte ciphertext in two 16-byte chunks:
psa_cipher_update(16 bytes)succeeds.psa_cipher_update(16 bytes)fails with BUFFER_TOO_SMALL despite the output buffer being 16 bytes (the value returned by PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(16)).Cause:
mbedtls_psa_cipher_update()called frompsa_cipher_update()computes the expected output size as:At the second psa_cipher_update() call:
unprocessed_len= 16input_length= 16block_length= 16Thus:
expected_output_size= (16 + 16) / 16 * 16 = 32 bytes.Because psa_cipher_update() expects to emit 32 bytes in one call, but the caller only provides a 16-byte output buffer (as suggested by the PSA macros), the function returns PSA_ERROR_BUFFER_TOO_SMALL.
Source code
@jnmeurisse Thank you very much for the reproducer and for the analysis. Indeed the output length will depend on the padding mode, expecting the same output length independently of the padding mode can't possibly be correct.
I haven't yet dug through the code, but that strikes me as strange:
16 bytes is a full block, why didn't the previous code emit it? From my memory of writing the initial version of that code (a long time ago, and it's been through a lot since then), I don't remember a reason to keep a full block in reserve, for any of the modes that we support. As the comment still says above the
expected_output_sizecalculation:In case there is padding, if you have a full block in the queue, update doesn't know if more data is coming and if it needs to emit all of it or just part of it, so keeps it until the next call.
To clarify and expand Janos's explanation:
Some ciphers process the input in fixed-size blocks (always 16 bytes in TF-PSA-Crypto, could be 8 bytes in Mbed TLS ≤3.6.x which supported DES). In these modes,
psa_cipher_update()cannot emit output until it has a full block of input. So for example, if you pass 20 bytes of input topsa_cipher_update()and then 20 more bytes, then the first call outputs 16 bytes (and stores the last 4 bytes of input in the operation object) and the second call outputs 16 bytes (with 8 bytes left in the operation object). A At the end,psa_cipher_{encrypt,decrypt}_finish()emits the output corresponding to the input that is left in the operation object.There is an additional complication, which is that there are ciphers where the last block requires special handling. Among the modes we support, the only such case is CBC-PKCS7 decryption, where the last block contains 0–15 bytes of payload, plus padding. (Encryption has no special handling of the last block of input, but the finish call may emit one extra block of output.) Since
psa_cipher_update()doesn't know whether it's called for the last time, if it's doing CBC-PKCS7 decryption and its input ends on a block boundary, it must store the whole last block of input.So let's look at the possible cases for a call to
psa_cipher_update()for CBC-PKCS7 decryption in terms of input lengthnpassed to the function and input lengthpin the operation object. In the operation object, there can be from 0 bytes (operation just started) to 16 bytes (the last call ended on a block boundary). The decryption process has(p + n)bytes of ciphertext available, and this starts on a block boundary. (It's an invariant that the start of the input left in the operation object is aligned on a block boundary.) Ifp + n > 16 * kthen there arekblocks of plaintext available for output immediately (i.e.16 * kbytes). Note that this is a strict inequality because the last full block available so far cannot be decrypted if it's the very last block of input. Given thatpsa_cipher_updatedoesn't know if there will be more input, it can only be sure that a block is not the last one if that block is not the last one so far, i.e. if there is at least one more byte of input already available. Letobe the output length:p + n == 0(i.e. empty input on an operation that has never had input), emit nothing (o = 0) and store nothing. (The easiest way to deal with that is to quit early ifn == 0.)p + nis a multiple of 16 but not 0, then emito = p + n - 16bytes of output and store 16 bytes in the operation. We haveo=n + (p - 16)≤n.p + nis not a multiple of 16, then emito = p + n - (p + n) % 16bytes of output and store(p + n) % 16bytes in the operation. We haveo=n + (p - (p + n) % 16)≤n + 15sincep≤ 16 and(p + n) % 16≤ 0.In all cases, we have
o≤n+block_size- 1. This fits the current definitions ofPSA_CIPHER_UPDATE_OUTPUT_SIZEandPSA_CIPHER_UPDATE_OUTPUT_MAX_SIZEwhich allowoto be up ton + block_size.The calculation of
expected_output_sizeinpsa_cipher_updateis more conservative:It doesn't apply the
- 16part of the output length calculation whenp + nis a multiple of 16. This calculation gives the naive result for block processing when each complete block can be processed immediately (and thus the operation object never stores a full block of unprocessed input), but it's off by one block whenoperation->ctx.cipher.unprocessed_len == operation->block_length.