Xilinx asu ecc standalone - #11053
Conversation
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #11053
Scan targets checked: wolfcrypt-port-bugs, wolfcrypt-rs-bugs, wolfssl-bugs, wolfssl-src
Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
|
2a34935 to
db9d0e3
Compare
Offload ECDSA/Ed25519/Ed448 sign/verify, ECDH, and ECIES (AES-GCM) to the ASU via the wolfSSL crypto callback; add wc_ecc_ctx_get_mac_salt and the ECC benchmark.
db9d0e3 to
0478f96
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #11053
Scan targets checked: wolfcrypt-bugs, wolfcrypt-port-bugs, wolfcrypt-rs-bugs, wolfcrypt-src, wolfssl-bugs, wolfssl-src
Findings: 21
21 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Findings are non-blocking.
| } | ||
|
|
||
| pubKeySz = 1U + (2U * (word32)keyLen); /* X9.63 uncompressed 0x04||Qx||Qy */ | ||
| need = pubKeySz + (word32)WC_ASU_ECIES_NONCE_SZ + msgSz + |
There was a problem hiding this comment.
🔴 [High] ECIES offload hardcodes the WOLFSSL_ECIES_GEN_IV blob layout with no build guard · Missing/incorrect platform conditionals
The offload always embeds a 12-byte nonce between the ephemeral key and the ciphertext, but ecc_ecies_total_size() (ecc.c:15384) only includes the nonce when WOLFSSL_ECIES_GEN_IV is defined. In a default or WOLFSSL_ECIES_STATIC_GCM_NONCE build the hardware blob is 12 bytes longer than software's, and wc_AsuEciesDecrypt mis-slices software-produced blobs.
Fix: Add #ifndef WOLFSSL_ECIES_GEN_IV #error ... #endif (and reject WOLFSSL_ECIES_OLD) at the top of asu_ecies.c.
| } | ||
|
|
||
| XMEMSET(req, 0, sizeof(*req)); | ||
| if (mp_to_unsigned_bin_len(key->k, req->key, (int)keyLen) < 0) { |
There was a problem hiding this comment.
🟠 [Medium] ECDSA sign marshals the blinded private scalar key-k instead of ecc_get_k() · API contract violations
With WOLFSSL_ECC_BLIND_K, key->k holds k XOR kb (ecc.c:377-386) and the real scalar is only available via ecc_get_k()/wc_ecc_key_get_priv(), which every other port uses. The ASU is handed a masked scalar, so it silently produces invalid signatures; the mp_iszero(key->k) guard at line 236 is also meaningless under blinding.
Fix: Use wc_ecc_key_get_priv(key) in place of key->k for both the zero check and the export.
|
|
||
| XMEMSET(req, 0, sizeof(*req)); | ||
| /* Our private scalar d, and the peer public point Qx||Qy, each curve-width. */ | ||
| if (mp_to_unsigned_bin_len(priv->k, req->privKey, (int)keyLen) < 0 || |
There was a problem hiding this comment.
🟠 [Medium] ECDH marshals the blinded private scalar priv-k instead of ecc_get_k() · API contract violations
priv->k is the blinded scalar under WOLFSSL_ECC_BLIND_K (ecc.c:377-386), so the ASU computes the shared secret from a masked value and silently returns a secret that does not match the peer's.
Fix: Use wc_ecc_key_get_priv(priv) for both the mp_iszero/mp_unsigned_bin_size checks and the export.
| XMEMSET(req, 0, sizeof(*req)); | ||
|
|
||
| /* Our private scalar; the ephemeral public key comes straight from the blob. */ | ||
| if (mp_to_unsigned_bin_len(privKey->k, req->rxKey, (int)keyLen) < 0) { |
There was a problem hiding this comment.
🟠 [Medium] ECIES decrypt marshals the blinded private scalar privKey-k instead of ecc_get_k() · API contract violations
privKey->k is the blinded scalar under WOLFSSL_ECC_BLIND_K (ecc.c:377-386), so the ASU derives the DEM key from a masked ECDH result and decryption fails for every message.
Fix: Use wc_ecc_key_get_priv(privKey) for both the mp_iszero check at line 389 and the export.
|
|
||
| wc_AsuCacheInvalidate(out, ctLen); | ||
|
|
||
| if (status != XST_SUCCESS) { |
There was a problem hiding this comment.
🟠 [Medium] ECIES decrypt ignores the ASU AdditionalStatus tag verdict and leaves unauthenticated plaintext in out · Incorrect error handling
addl is collected from wc_AsuTransact but never inspected, unlike wc_AsuCipherGcm (asu_cipher.c:519-528) which requires addl == XASU_AES_TAG_MATCHED, zeroes out on mismatch, and returns AES_GCM_AUTH_E. A GCM tag failure here is either missed or reported as WC_HW_E, and the ASU-written plaintext stays in the caller's buffer.
Related known finding #6623 (similar but distinct): Both mishandle a hardware-operation result and can expose plaintext on failure, but this is ASU ECIES decrypt's ignored GCM tag verdict in wc_AsuEciesDecrypt, whereas #6623 is QAT's ignored synchronous perform-op status in IntelQaSymCipher. The faulting results, root checks, functions, and required patches differ.
Fix: Require the tag-matched AdditionalStatus alongside XST_SUCCESS, and on failure ForceZero out and return AES_GCM_AUTH_E.
| /* WOLFSSL_BENCH_ECC_ALL: let a plain 'bench all' run sweep every | ||
| * compiled-in curve, not just P-256, so HW/SW compare across all. */ | ||
| #ifdef WOLFSSL_BENCH_ECC_ALL | ||
| if ((bench_asym_algs & BENCH_ECC_ALL) || bench_all) { |
There was a problem hiding this comment.
🔵 [Low] WOLFSSL_BENCH_ECC_ALL drops all ECC benchmarks from a plain 'bench all' in FIPS builds · Logic errors
With WOLFSSL_BENCH_ECC_ALL defined, the default run (bench_all == 1, bench_asym_algs == 0) now enters the curve-sweep branch, which under HAVE_FIPS/HAVE_SELFTEST only prints "not supported in FIPS mode" and skips the else branch that used to benchmark P-256.
Fix: Gate the || bench_all term on !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST).
| ret = WC_HW_E; | ||
| goto out; | ||
| } | ||
| XMEMCPY(req->txKey, msg + 1, 2U * (word32)keyLen); |
There was a problem hiding this comment.
🔴 [High] ECIES decrypt offload skips validation of the attacker-supplied ephemeral public point · Cryptographic correctness
The ephemeral point is copied raw out of the ciphertext blob into req->txKey and handed to the ASU with no on-curve or point-at-infinity check. The software path it replaces validates the same point via wc_ecc_import_x963_ex (ecc.c:16104, "treat as untrusted"), so the offload exposes a static ECIES receiver key to invalid-curve and small-subgroup recovery using the GCM pass/fail oracle.
Fix: Import the ephemeral point with wc_ecc_import_x963_ex (or call wc_ecc_point_is_on_curve plus an infinity check) before submitting the request, declining on failure.
| ret = 0; | ||
|
|
||
| out: | ||
| WC_FREE_VAR_EX(req, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
There was a problem hiding this comment.
🔵 [Low] ECDH private scalar and shared secret not scrubbed before free · Missing ForceZero
AsuEcdhReq holds the marshalled private scalar in req->privKey and the raw ECDH shared secret in req->secret. WC_FREE_VAR_EX is a plain XFREE (or a no-op on the stack variant), so both secrets survive in reusable memory; the PR's own wc_AsuEccSign scrubs its request with ForceZero first.
Related known finding #7437 (similar but distinct): Both free temporary private-key and shared-secret material without wiping it, but this is wc_AsuEcdh's ASU ECDH request object rather than IntelQaDhAgreeFree's QAT DH buffers. The operations, port-specific root paths, and required patches differ.
Fix: Add ForceZero(req, sizeof(*req)) before WC_FREE_VAR_EX at the out: label.
| ret = 0; | ||
|
|
||
| out: | ||
| WC_FREE_VAR_EX(req, NULL, DYNAMIC_TYPE_TMP_BUFFER); |
There was a problem hiding this comment.
🔵 [Low] ECIES decrypt leaves the ECC private scalar in freed memory · Missing ForceZero
privKey->k is marshalled into req->rxKey (line 445) and the request is released with WC_FREE_VAR_EX without zeroing, leaving the receiver's long-term ECC private key in reusable heap or stack memory on both the success and error paths.
Related known finding #4895 (similar but distinct): Both release a temporary buffer containing private-key material without zeroization, but this is marshalled ECIES receiver scalar data in wc_AsuEciesDecrypt rather than DER-decoded Falcon key bytes in wc_Falcon_PrivateKeyDecode. They have distinct operations, functions, and cleanup fixes.
Fix: Add ForceZero(req, sizeof(*req)) before WC_FREE_VAR_EX at the out: label.
| } | ||
|
|
||
| pubKeySz = 1U + (2U * (word32)keyLen); /* X9.63 uncompressed 0x04||Qx||Qy */ | ||
| need = pubKeySz + (word32)WC_ASU_ECIES_NONCE_SZ + msgSz + |
There was a problem hiding this comment.
🔵 [Low] ECIES offload hardcodes the GEN_IV blob layout, diverging from software in other IV modes · Cryptographic correctness
The offload always embeds and consumes a 12-byte GCM nonce in the blob. ecc_ecies_total_size (ecc.c:15384) includes the nonce only under WOLFSSL_ECIES_GEN_IV, so in a WOLFSSL_ECIES_STATIC_GCM_NONCE build an ASU-produced blob is undecryptable by wolfSSL software and vice versa, with no build-time guard rejecting the mismatch.
Fix: Gate the ECIES offload on WOLFSSL_ECIES_GEN_IV (returning CRYPTOCB_UNAVAILABLE otherwise) so the hardware and software wire formats always agree.
No description provided.