Skip to content

Fix for Blind Signing Freeze on Aave EIP-2612 Permit Bundled Transactions #885

@pdiomede

Description

@pdiomede

Description

There is a specific blind signing freeze issue in the Ethereum app (firmware 2.5.1) when handling Aave's "Approve with signed message" flow for supplying assets like USDT. The freeze occurs during the post-checkmark dialog due to the app's parser struggling with the complex, nested calldata generated by EIP-2612 permit bundling (embedded signature data in a single supply transaction).

Your environment

  • Device: Ledger Nano X (firmware 2.5.1)
  • App: Ethereum (latest, reinstalled)
  • Wallet: Rabby (v0.93.53+), MetaMask
  • OS/Browser: macOS 15.0 (M4), Chrome 141/Brave
  • Network: Arbitrum

Steps to reproduce

  • Update to firmware 2.5.1 and latest ETH app (v2.3.0+).
  • Enable blind signing in ETH app settings.
  • Connect Ledger Nano X to Rabby/MetaMask via Chrome on macOS.
  • In Aave: Supply 100 USDT > Select "Approve with signed message" > Proceed to sign.
  • On device: Approve initial prompt (double-press both buttons) > Freezes on subsequent screen.

Expected behaviour

Approve the token spending and then supply the tokens (but the device freezes).

Actual behaviour

After double clicking the first dialog on the Ledger Nano X device, I the next confirmation screen the device freezes.

Workaround

Switch in Aave to Transaction mode in the Approve with Signed message option (click on the the gear icon)

Image

Logs

N/A but I can provide eventually, just tell me what you need.

Proposed solution

To resolve the blind signing freeze during Aave's EIP-2612 permit-bundled transactions (e.g., "Approve with signed message" for USDT supply), we need to optimize the calldata handling in the signing flow. The issue stems from the Ethereum app's parser and hasher struggling with longer, nested payloads (~2-3x standard calldata length due to embedded permit signatures), leading to watchdog timeouts or buffer overflows during on-device processing.

This can be fixed by:

  • Introducing chunked hashing for large calldata in the blind signing path to prevent long-running loops.
  • Adding yield points (e.g., I/O status checks) in the UI rendering and parsing to avoid freezes on confirmation dialogs.
  • Optionally, increasing buffer sizes for calldata if overflows are confirmed (via debug logs).

These changes are backward-compatible, minimally invasive, and can be tested with a mock Aave permit transaction.

Implementation Steps

  1. Modify Calldata Hashing in APDU Parser (src/apdu_parser.c):
    Update the handleSignTransaction() or equivalent APDU handler to use incremental updates for Keccak hashing when blind signing is enabled. This breaks long payloads into chunks, yielding control periodically to prevent hangs.

    Before (potential bottleneck):

    if (blind_sign_enabled) {
        cx_sha3_t sha3;
        cx_sha3_init(&sha3, 256);
        cx_sha3_update(&sha3, tx_data, tx_len);  // Single call can freeze on large tx_len
        ux_blind_sign_prompt();  // UI after hash
    }

    After:

    if (blind_sign_enabled) {
        cx_sha3_t sha3;
        cx_sha3_init(&sha3, 256);
        const uint32_t chunk_size = 1024;  // Tune based on SE limits
        for (uint32_t offset = 0; offset < tx_len; offset += chunk_size) {
            uint32_t this_chunk = MIN(chunk_size, tx_len - offset);
            cx_sha3_update(&sha3, tx_data + offset, this_chunk);
            io_seproxyhal_general_status();  // Yield to prevent timeout
        }
        ux_blind_sign_prompt();  // Safer post-chunked hash
    }
  2. Enhance UI Rendering for Confirmation Dialogs (src/ui/screens.c or src/ui/blind_sign_ui.c):
    In the post-prompt dialog renderer (e.g., ui_display_blind_confirm()), add timeouts or split rendering steps for the "next dialog" (where the freeze occurs). This ensures the UI doesn't block on data review.

    Example Addition:

    // In blind confirm UI loop
    for (uint32_t i = 0; i < num_steps; i++) {
        ux_step_render(i);  // Render one step at a time
        if (io_seproxyhal_spi_prepare(0)) {  // Check for user input or timeout
            break;
        }
    }
  3. Update Transaction Parser for Better Validation (src/eth_tx_parser.c):
    Add a pre-check for calldata length in parseTransactionData() to warn or chunk if exceeding a threshold (e.g., 4KB), falling back to clear signing if possible.

    Snippet:

    if (calldata_len > MAX_SAFE_CALLDATA) {
        // Log warning or prompt user for blind override
        return ERR_TX_TOO_LONG;
    }
  4. Buffer Adjustments (include/os_io_seproxyhal.h):
    If logs show overflows, increase G_io_seproxyhal_spi_buffer from default (e.g., 256 bytes) to 512 bytes for signing paths.

This should fully mitigate the freeze while improving robustness for future complex txs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions