Skip to content

Update specs to PSA 1.4 + Low/Info fenrir fixes#19

Merged
dgarske merged 24 commits into
wolfSSL:masterfrom
danielinux:PSA-1.4
Jun 10, 2026
Merged

Update specs to PSA 1.4 + Low/Info fenrir fixes#19
dgarske merged 24 commits into
wolfSSL:masterfrom
danielinux:PSA-1.4

Conversation

@danielinux

Copy link
Copy Markdown
Member

Added support for PSA 1.4 including PQC algorithms.

aab839e chacha: remove dormant XChaCha20-Poly1305 helpers
c93281b build-matrix: survive feature-off builds and cover the new 1.4 features
956b9d4 tests/ci: PSA 1.4 coverage suite, CHANGELOG
aa470dd crypto: Ascon, XChaCha20-Poly1305 and SP800-108 counter KDFs
73f0f54 psa: wire PQC and 1.4 signature features into key storage and dispatch
8ef3aed psa: KEM front-end, XOF, AES-KW, LMS/XMSS verify, 1.4 stubs
6390a47 pqc: seed-based ML-DSA and ML-KEM backends per PSA 1.4 PQC extension
1ab901e headers: complete PSA 1.4 + PQC extension macro/declaration surface
8ac5c34 build: enable ML-KEM, LMS/XMSS verify-only, Ascon, XChaCha, AES-KW, CMAC-KDF

  • Fenrir fixes:

3ef39c4 F-3654: fix dead out_len guard in wolfpsa_aead_encrypt_final ChaCha branch
bbc9206 F-3655: remove dead psa_tls_prf helpers with no callers
e89506b F-3656: remove dead psa_chacha20_poly1305_check_* helpers with no callers
889910a F-3862: add SHAKE256_512 to hash engine so psa_sign/verify_message works for Ed448PH
6c7f12b F-4090: use PSA_BITS_TO_BYTES instead of key_bits/8 in wc_ecc_make_key_ex call
930f0b3 F-4096: reject PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG in wolfpsa_aead_setup
0f67845 F-4560: add wc_ecc_make_pub_ex to psa_asymmetric_export_public_key_ecc and wire it in
38d943f F-4561: check output buffer size before psa_cipher_generate_iv in psa_cipher_encrypt
f8c4f11 F-3653: check ciphertext buffer size before psa_aead_finish to avoid modifying output on BUFFER_TOO_SMALL
19b51ca F-3658: zeroize padded plaintext block in CBC-PKCS7 encrypt path
046f3d0 F-3660: fix algorithm confusion in psa_xchacha20_poly1305_encrypt/decrypt
6c8db18 F-3859: add test_hash_compare covering success, mismatch, wrong-length, and non-hash-alg paths
4e35d84 F-3860: add direction-mismatch tests for psa_aead_finish and psa_aead_verify

…_verify

Test that psa_aead_finish on a decrypt context and psa_aead_verify on an
encrypt context both return PSA_ERROR_BAD_STATE, covering the direction
enforcement checks that had zero test coverage.
…h, and non-hash-alg paths

The ConstantCompare mismatch-detection branch was reachable only by calling
psa_hash_compare before psa_crypto_init (PSA_ERROR_BAD_STATE) or with a NULL
reference hash (PSA_ERROR_INVALID_ARGUMENT). The success path and the critical
INVALID_SIGNATURE path were entirely untested, so a mutation of the ConstantCompare
check or the hash_length guard would survive undetected.
…rypt

Both xchacha20 helpers checked alg != PSA_ALG_CHACHA20_POLY1305 (passing
IETF ChaCha20), but then invoked wc_XChaCha20Poly1305_Encrypt/Decrypt
and required a 24-byte XChaCha20 nonce.  Invert the check to reject
PSA_ALG_CHACHA20_POLY1305 so the standard IETF algorithm identifier
cannot route to the XChaCha20 primitive.
Mirror the decrypt branch's wc_ForceZero pattern: introduce a
cbc_pkcs7_encrypt_done label so the stack buffer holding the last
partial plaintext block plus PKCS7 padding is always scrubbed before
the function returns, on all three exit paths (NOT_SUPPORTED, wolfCrypt
error, and success).
…modifying output on BUFFER_TOO_SMALL

The ciphertext size check (ciphertext_size < out_len + tag_length) ran
after psa_aead_finish had already written plaintext_length bytes of
ciphertext into the caller's buffer, violating the PSA Crypto API
requirement that BUFFER_TOO_SMALL must not modify output buffers.

Add an early check using wolfpsa_aead_tag_length before calling
psa_aead_finish, using overflow-safe subtraction. The post-finish check
is kept as a defence-in-depth assertion.
…_cipher_encrypt

iv_len is deterministic from wolfpsa_cipher_iv_length before any side-effectful
call, so the output_size < iv_len check was needlessly ordered after
psa_cipher_generate_iv, which consumed entropy and mutated operation state even
when the caller's buffer was too small.  Move the check ahead of the generate
call so BUFFER_TOO_SMALL is returned without drawing from the RNG.
…c and wire it in

psa_asymmetric_export_public_key_ecc was dead code missing wc_ecc_make_pub_ex
in the key-pair branch, which would cause wc_ecc_export_x963 to fail on a
private-only key. Add the missing call (matching the verify path at psa_ecc.c:247
and the inline block it replaces in psa_key_storage.c), add a forward declaration,
and replace the duplicated inline block with a call to the function.

Add test_ecc_export_public_key to cover psa_export_public_key on a SECP R1 key pair.
…aead_setup

Wildcard AEAD algorithms (PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG set) are
policy-only constructs per the PSA Crypto spec and must not be accepted as
the algorithm for an AEAD operation. Without this check, the flag bits were
silently ignored and min_len was used as the concrete tag length.
…y_ex call

Integer division truncates for P-521 (521/8=65), but the correct byte
length is 66 (PSA_BITS_TO_BYTES(521)=66). The rest of the function already
uses PSA_BITS_TO_BYTES consistently; align the wc_ecc_make_key_ex keysize
hint to match.
…rks for Ed448PH

PSA_ALG_SHAKE256_512 was missing from psa_hash_engine.c, causing
psa_hash_compute() to return PSA_ERROR_NOT_SUPPORTED whenever
psa_sign_message() or psa_verify_message() was called with
PSA_ALG_ED448PH.  Add PSA_ALG_SHAKE256_512 cases (guarded by
WOLFSSL_SHAKE256) to all seven switch statements in psa_hash_engine.c:
check_alg_supported, get_size, cleanup, setup, update, finish, and
clone.  Add test_ed448_sign_message to cover the full
psa_sign_message/psa_verify_message roundtrip for Ed448PH.
…lers

The three WOLFSSL_LOCAL check helpers were never called; psa_aead.c
performs the equivalent validation inline.  Removing them eliminates
dead flash and prevents undetected mutations.
src/psa_tls_prf.c and wolfpsa/psa_tls_prf.h defined five WOLFSSL_LOCAL
TLS 1.3 PRF helpers (psa_tls_prf_check_alg_supported, psa_tls13_prf,
psa_tls13_hkdf_extract, psa_tls13_hkdf_expand, psa_tls13_hkdf_expand_label)
that had no callers anywhere in the codebase. The header was only included
by the implementation file itself. Same pattern as F-3429.
…ranch

The check `out_len < ctx->tag_length` was dead code: psa_chacha20_poly1305_encrypt
always sets *ciphertext_length = plaintext_length + tag_size on success, so
out_len equals input_length + tag_length and the condition reduces to
input_length < 0, impossible for size_t.  The correct bound to protect the
subsequent XMEMCPY(tag, tmp + input_length, tag_length) is
out_len < input_length + tag_length.
…MAC-KDF

Enable the wolfCrypt backends needed for PSA 1.4: WOLFSSL_HAVE_MLKEM,
verify-only LMS/XMSS, Ascon (experimental opt-in), XChaCha, AES key wrap
and CMAC KDF. Add ascon.c to the wolfCrypt source list.

Quarantine the legacy ML-KEM and LMS/XMSS modules behind #if 0: they
target headers and APIs that no longer exist on wolfSSL master
(mlkem.h, lms.h/xmss.h, psa_ml_kem_parameter_t) and are rewritten in
the PSA 1.4 work. Replace the stale WOLFSSL_HAVE_KYBER guard in
psa_pq.c and add psa_pqc_internal.h with the internal PQC backend
contracts.
Fill the previously empty PQC macros in crypto-pqc.h with the exact
spec-defined implementations (ML-DSA/ML-KEM/SLH-DSA classifiers, key
types, HashML-DSA encodings) and drop the nonstandard
psa_ml_dsa_parameter_t / PSA_ML_DSA_PARAMETER_* convention.

crypto_values.h: add 1.3/1.4 algorithm and policy values (EDDSA_CTX,
KW/KWP, XOF algs, ECIES_SEC1, Ascon AEAD/hash, XChaCha20, SP800-108
KDFs, WPA3-SAE, WRAP/UNWRAP usage flags) and adopt the 1.4 example
definitions of the sign-classification macros.

crypto_sizes.h: ML-DSA/ML-KEM arms for sign/export sizes, KEM
ciphertext sizes, key-wrap output sizes and the hash-suspend format
constants; raise the max-size constants that ML-DSA now dominates.

crypto.h/crypto_struct.h: declare psa_encapsulate/psa_decapsulate, the
four *_with_context signature functions, key wrapping, the XOF
operation API, hash suspend/resume, psa_attach_key and
psa_check_key_usage; remove the legacy psa_ml_dsa_*/psa_lms_verify/
psa_xmss_verify declarations and add psa_xof_operation_t.

Quarantine the legacy ML-DSA backend and its psa_api_test coverage
pending the seed-based rewrite.
Rewrite the ML-DSA backend around the spec key format: key pairs are
the 32-byte FIPS 204 seed xi with bits 128/192/256, public keys are raw
pk bytes. Sign/verify dispatch covers hedged and deterministic pure
ML-DSA (wc_MlDsaKey_SignCtx / SignCtxWithSeed with a zero rnd) and
HashML-DSA over pre-computed digests (SignCtxHash/VerifyCtxHash), with
signing contexts up to 255 bytes.

Rewrite the ML-KEM backend against wc_mlkem.h: key pairs are the
64-byte d||z seed (FIPS 203) expanded via wc_MlKemKey_MakeKeyWithRandom
on each use; encapsulation accepts raw public keys or seeds and
decapsulation enforces exact ciphertext length, relying on ML-KEM
implicit rejection.

Breaking change: the legacy psa_ml_dsa_* public API and its
level-as-bits (2/3/5) convention are gone.
psa_kem.c: psa_encapsulate/psa_decapsulate returning the ML-KEM shared
secret as a new key created from caller attributes; ciphertext is
staged locally so no partial result escapes on failure.

psa_xof.c: incremental SHAKE128/256 XOF operations. wolfCrypt's
Shake Absorb finalizes padding per call, so update() accumulates input
and absorbs once at first output(); output is served at byte
granularity over block-wise squeezes. Ascon XOFs report NOT_SUPPORTED.

psa_key_wrap.c: psa_wrap_key/psa_unwrap_key with AES-KW (RFC 3394),
mapping the integrity-check failure to PSA_ERROR_INVALID_SIGNATURE;
KWP reports NOT_SUPPORTED (no wolfCrypt backend).

psa_lms_xmss.c: verify-only LMS/HSS and XMSS/XMSS^MT backends importing
raw public keys (XMSS retries the XMSS^MT OID table on lookup miss).

psa_api_stub.c: NOT_SUPPORTED stubs for the interruptible operations,
psa_attach_key and hash suspend/resume; trivial max-ops accessors; and
psa_generate_key_custom / psa_key_derivation_output_key_custom
delegating wrappers that accept only default parameters.
psa_key_storage.c: ML-DSA/ML-KEM key lifecycle (generate seeds via the
PQC backends, import validation for seed and raw public formats, bit
inference from public-key lengths, public-key derivation from seeds in
psa_export_public_key), LMS/HSS/XMSS public-key import, XChaCha20 keys,
WRAP/UNWRAP usage flags and the new psa_check_key_usage.

psa_asymmetric_api.c: refactor sign/verify into context-aware workers
and add the four *_with_context entry points (context <= 255 bytes,
non-empty context only for EDDSA_CTX/Ed25519ph/Ed448ph/Ed448 pure and
the ML-DSA family). ML-DSA dispatches before the pre-hash step so raw
messages are signed directly; HashML-DSA pre-hashes via
PSA_ALG_GET_HASH; pure ML-DSA is rejected in the hash entry points.
LMS/HSS/XMSS/XMSS^MT verify through psa_verify_message. Policy gains
the HashML-DSA ANY_HASH wildcard and the 1.4 rule that ECDSA and
deterministic ECDSA are equivalent for verification.

psa_ed25519_ed448.c: thread the signing context through to wolfCrypt
(Ed25519ctx via wc_ed25519_sign_msg_ex, Ed448 context parameters).

psa_pq.c: ML-DSA sizes are now 128/192/256; LMS/HSS/XMSS/XMSS^MT are
public-key-only with bits = hash output length; SLH-DSA reports
NOT_SUPPORTED.

wolfpsa.map: export the new 1.4 entry points, drop psa_ml_dsa_*.
Hash engine: Ascon-Hash256 (one-shot and multipart, POD clone).
AEAD: one-shot XChaCha20-Poly1305 (24-byte nonce) and Ascon-AEAD128
(16-byte key/nonce/tag); multipart setup reports NOT_SUPPORTED for
both. Key derivation: SP800-108r1 counter-mode KDF in HMAC and
AES-CMAC variants per the PSA 1.4 construction (4-byte counter, label,
zero separator, context, output length in bits; the CMAC variant mixes
in K(0)), with L snapshotted from the operation capacity at first
output.

Headers: PSA_HASH_LENGTH arm for Ascon-Hash256, AEAD nonce lengths for
XChaCha20/Ascon (nonce max now 24), default-length-tag cases for the
new AEAD algorithms and PSA_KEY_DERIVATION_INPUT_CONTEXT.
Add nine feature-area coverage tests (ML-DSA, ML-KEM and the KEM API,
SHAKE XOF, AES-KW key wrapping, signature contexts, LMS/XMSS verify-only
with wolfSSL KAT vectors, Ascon and XChaCha20-Poly1305 KATs, SP800-108
counter KDFs, and 1.4 miscellany: stubs, custom wrappers,
psa_check_key_usage, ECDSA verify-equivalence, size macros), wire them
into the test Makefile and the CI workflow, and document the 1.4
upgrade in the CHANGELOG.

Fix flagged by the Ascon test: map ASCON_AUTH_E to
PSA_ERROR_INVALID_SIGNATURE in the Ascon-AEAD128 decrypt path.
Running the build-config matrix against the 1.4 changes exposed
-Werror failures when the new backends are compiled out: unused locals
in psa_wrap_key/psa_unwrap_key without HAVE_AES_KEYWRAP, an unused
key_bits in psa_decapsulate without WOLFSSL_HAVE_MLKEM, and an unused
bits parameter in psa_pq_check_key_size_valid when every PQC backend
is off. Fix all three.

Sync build-variant.sh's baseline with the new wolfCrypt knobs (ML-KEM,
verify-only LMS/XMSS, Ascon + experimental opt-in, AES-KW + AES
direct, XChaCha, CMAC-KDF) and add matrix lanes that disable each new
feature. Companion removals for existing lanes: chacha20-poly1305 also
drops XChaCha, cmac drops the CMAC KDF, sha3-ed448 drops ML-KEM (it
needs SHA-3/SHAKE), and AES key wrap explicitly carries
WOLFSSL_AES_DIRECT, which wolfSSL requires for HAVE_AES_KEYWRAP.

All 35 lanes build clean locally.
psa_xchacha20_poly1305_encrypt/decrypt sat behind HAVE_XCHACHA with no
callers since their introduction and only started compiling when the
PSA 1.4 work enabled XChaCha. The live XChaCha20-Poly1305 path is the
one-shot implementation in psa_aead.c; drop the dead pair and their
declarations.
@danielinux danielinux self-assigned this Jun 10, 2026

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #19

Scan targets checked: none
Failed targets: wolfpsa-bugs, wolfpsa-src

⚠️ Review incomplete — one or more scan targets failed before findings could be produced. See the Fenrir PR review detail page for logs.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #19

Scan targets checked: none
Failed targets: wolfpsa-bugs, wolfpsa-src

⚠️ Review incomplete — one or more scan targets failed before findings could be produced. See the Fenrir PR review detail page for logs.

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Code Review

Scan type: reviewOverall recommendation: COMMENT
Findings: 9 total — 8 posted, 1 skipped
8 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [Low] psa_check_key_usage declares variables after a statement (C89 violation)src/psa_key_storage.c:2188-2196
  • [Low] Mid-block declaration in psa_xof_output and non-conforming brace stylesrc/psa_xof.c:421
  • [Low] Test leaks XOF operation after failed psa_xof_set_context (misleading comment)test/psa_server/psa_xof_test.c:test_set_context_shake
  • [Low] SP800-108 input-step validation claims single-set but does not enforce itsrc/psa_key_derivation.c:381-392
  • [Low] psa_kem.c uses memcpy instead of XMEMCPYsrc/psa_kem.c:195
  • [Low] Dead empty if-block in psa_encapsulatesrc/psa_kem.c:123-125
  • [Low] LMS/XMSS verify: redundant ret==-1 branch and lossy error mappingsrc/psa_lms_xmss.c:175-180
  • [Low] psa_export_public_key runs ECC export on PQC key types before PQC branch overwrites statussrc/psa_key_storage.c:1841-1957

Skipped findings

  • [Medium] Large 1568-byte ciphertext staging buffer on the stack in psa_encapsulate

Review generated by Skoll

Comment thread src/psa_key_storage.c
Comment thread src/psa_xof.c Outdated
Comment thread test/psa_server/psa_xof_test.c
Comment thread src/psa_key_derivation.c
Comment thread src/psa_kem.c Outdated
Comment thread src/psa_kem.c Outdated
Comment thread src/psa_lms_xmss.c
Comment thread src/psa_key_storage.c
@dgarske dgarske assigned danielinux and unassigned dgarske Jun 10, 2026
@danielinux danielinux requested a review from dgarske June 10, 2026 20:51
@danielinux danielinux assigned dgarske and unassigned danielinux Jun 10, 2026
@dgarske dgarske removed their assignment Jun 10, 2026

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Code Review

Scan type: reviewOverall recommendation: COMMENT
Findings: 4 total — 3 posted, 1 skipped
3 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [Medium] SHAKE256_512 reuses WOLFSSL_SHA3-gated union member but is gated on WOLFSSL_SHAKE256src/psa_hash_engine.c:106-114, 208-212, 271, 334, 450, 568, 685, 883
  • [Low] Unreachable XChaCha/Ascon branches in wolfpsa_aead_check_keysrc/psa_aead.c:149-168
  • [Low] SP800-108 L field derived from capacity; default capacity yields non-interoperable outputsrc/psa_key_derivation.c:1611-1618

Skipped findings

  • [Low] Large stack buffer in psa_encapsulate; unused when ML-KEM disabled

Review generated by Skoll

Comment thread src/psa_aead.c Outdated
Comment thread src/psa_key_derivation.c
Comment thread src/psa_hash_engine.c
…docs

- aead: remove unreachable XChaCha20-Poly1305/Ascon-AEAD128 branches from
  wolfpsa_aead_check_key; both algorithms are one-shot only and rejected
  by wolfpsa_aead_setup before the key check, and the one-shot paths
  validate keys inline. Left a note so they are not re-added.
- kdf: document that SP800-108 counter-mode binds [L]_4 to the operation
  capacity per PSA 1.4 §10.8, so callers must set_capacity() to the exact
  total output before the first read to interoperate with implementations
  that derive L from the requested length.
@dgarske dgarske merged commit a4d1187 into wolfSSL:master Jun 10, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants