Cryptnox smart card with an Ethereum transaction being filtered on-card – JavaCard custom development case study
Custom DevelopmentHardware Wallet

JavaCard Custom Development for On-Card EVM Transaction Filtering

A case study from Cryptnox's custom smart card development practice: a JavaCard applet with native code on an NXP JCOP 4.5 P71 secure element that parses EIP-1559 transactions, enforces an ERC-20 whitelist and per-transaction limits, and only then signs, in about 48 ms.

Cryptnox SA, Geneva  ·  Custom secure element development

In one paragraph

Most hardware wallets sign whatever digest the host hands them. This applet moves the transaction policy onto the card: the host streams the raw EIP-1559 payload, the card RLP-parses it, checks the destination against up to five whitelisted ERC-20 contracts, checks the function selector and the amount, computes the Keccak-256 digest itself and signs with secp256k1. No whitelist entry means no signature. The same whitelist gates EIP-712 typed-data signing, and the card also carries a full Ed25519 implementation.

Most hardware wallets protect private keys, but many still rely on the host application to decide whether a transaction is safe. A phone, desktop app, browser extension, or backend system prepares the transaction, displays a summary, and asks the secure element to sign. That model protects the key, but the policy often remains outside the card.

For high-value Web3, fintech, treasury, and regulated wallet use cases, that is not always enough.

This JavaCard custom development project demonstrates a stronger model: an EVM Transaction Filtering Applet running on an NXP JCOP 4.5 P71 secure element (a Common Criteria EAL6+ certified platform), using NXP SecureBox native C to perform Ethereum-specific cryptography and transaction policy enforcement directly on the card. Cryptnox's custom smart card development services focus precisely on this kind of advanced secure element work, including JavaCard applets, SecureBox native C, Keccak-256, blockchain signing, and full-stack integration.

The result is a secure element that does more than sign. It can hash, parse, filter, and enforce a transaction policy before releasing an ECDSA signature.

Why On-Card Transaction Filtering Matters

In a conventional wallet architecture, the secure element may only receive a 32-byte digest and return a signature. This is simple and efficient, but it means the card cannot independently verify what it is signing. A compromised host could attempt to replace the destination, alter calldata, request unlimited approvals, or present a misleading transaction summary.

The EVM Transaction Filtering Applet changes that trust boundary.

Instead of blindly signing an externally prepared digest, the card receives the EIP-1559 transaction payload, checks that it matches a strict whitelist policy, computes the Ethereum Keccak-256 digest internally, and only then signs with the private key stored on-card.

Ethereum EIP-1559 introduced transaction type 0x02, where the signature is over keccak256(0x02 || rlp([...])), including fields such as chain ID, nonce, fee parameters, gas limit, destination, amount, data, and access list. This project implements that signing flow directly inside the secure element for EIP-1559 transactions.

What Was Built

The applet is a SecureBox native signing applet for NXP JCOP 4.5 P71. It performs:

  • On-card Keccak-256 hashing using the Ethereum raw Keccak variant.
  • On-card ECDSA/secp256k1 signing for EIP-1559 Ethereum transactions.
  • On-card RLP transaction filtering before signature release.
  • A multi-entry whitelist for up to five ERC-20 contract addresses.
  • Per-entry transaction amount limits.
  • Secure key generation, private key injection, and key wiping through SCP03-protected commands.
  • EIP-712 typed data signing for structured messages such as Permit2 approvals, Uniswap orders, and governance votes.
  • A complete Ed25519 implementation, including sign and verify, SHA-512, fixed-base comb multiplication, and variable-base wNAF multiplication.

Java Card is designed for secure elements and smart cards that host multiple applications on resource-constrained tamper-resistant devices, while keeping compatibility with standards such as ISO 7816 and GlobalPlatform. This project extends that model with native cryptographic code for blockchain-specific primitives that are not normally available through standard JavaCard APIs. The same secure-element family powers the Cryptnox hardware wallet card, and the host-side Cryptnox SDKs show how an application talks to a card applet over NFC or a contact reader.

Supported EVM Transaction Type

The applet supports EIP-1559 type 0x02 transactions only.

The card signs:

keccak256(0x02 || RLP([
  chainId,
  nonce,
  maxPriorityFeePerGas,
  maxFeePerGas,
  gasLimit,
  to,
  value,
  data,
  accessList
]))

The returned signature is:

v || r || s

Where:

  • Byte 0: v, the yParity value: 0 or 1
  • Bytes 1–32: r, 32 bytes, big-endian
  • Bytes 33–64: s, 32 bytes, big-endian, normalized to low-S

This format is directly aligned with Ethereum-style ECDSA transaction signatures.

Secure Default: No Whitelist, No Signature

The most important policy rule is simple:

Critical Policy Rule

If no whitelist entries are configured, the card refuses to sign. A card must be explicitly configured before it can authorize any EVM contract call.

That fail-closed behavior is essential. The applet is designed for controlled signing environments, not open-ended generic wallet signing.

Each whitelist entry contains:

flags(1) + contract address(20) + max_amount(12)

That gives each entry:

  • A 20-byte ERC-20 contract address.
  • A uint96 per-transaction amount limit.
  • Policy flags controlling selector and amount enforcement.

The applet supports up to five entries, which is enough for many controlled fintech, treasury, stablecoin, and operational signing use cases.

Whitelist Policy for ERC-20 Transactions

For each EIP-1559 transaction, the card enforces the following rules:

  • to must match one configured 20-byte contract address
  • value must be 0
  • data must be non-empty
  • accessList must be empty

This means the card refuses native ETH transfers and only allows contract calls to approved destinations.

When the ERC20_SELECTORS flag is enabled, the card only allows the standard ERC-20 function selectors for:

  • transfer(address,uint256)
  • approve(address,uint256)
  • transferFrom(address,address,uint256)

ERC-20 defines common token functions including transfer, approve, allowance, and transferFrom for fungible token contracts. In this applet, those familiar ERC-20 methods become enforceable policy targets inside the secure element.

Per-Transaction Amount Limits

When the AMOUNT_LIMIT flag is enabled, the card extracts the ERC-20 amount directly from calldata and compares it with the configured uint96 max_amount.

The amount is extracted from:

transfer(address,uint256):              calldata offset 36
approve(address,uint256):               calldata offset 36
transferFrom(address,address,uint256):  calldata offset 68

If the amount exceeds the configured limit, the transaction is rejected before signing.

This is especially useful for stablecoin payments, controlled treasury operations, DeFi approvals, and enterprise wallets where "sign anything with this key" is too broad.

EIP-712 Typed Data Signing

The applet also supports EIP-712 typed structured data signing.

EIP-712 defines a structured data hashing and signing standard for typed messages, including domain-separated signing for smart contract interactions. In this project, the host pre-computes:

  • domainSeparator
  • structHash

Then sends the card:

verifyingContract(20) + domainSeparator(32) + structHash(32)

The card checks that the verifyingContract address is present in the same whitelist used for EIP-1559 transaction signing. If the contract is allowed, the card computes:

keccak256("\x19\x01" || domainSeparator || structHash)

Then signs the digest with ECDSA/secp256k1.

This makes the same whitelist policy usable for both transaction signing and typed data signing. No separate EIP-712 whitelist is required.

Where This Makes Sense: Practical Use Cases

01Stablecoin Payment Cards and Controlled Spending Wallets

A fintech or issuer may want to let users spend USDC, EURC, or another ERC-20 stablecoin, but only through approved contracts and within transaction limits.

The applet can be configured so the card signs only calls to selected stablecoin contracts, with a maximum amount per transaction. Native ETH transfers are rejected. Unknown contracts are rejected. Empty calldata is rejected.

This is a strong fit for payment cards, crypto spending products, and controlled Web3 wallets where the user experience must be simple but the signing rules must remain strict. Cryptnox's Card Wallet as a Service model already focuses on white-label crypto wallet cards for financial institutions and issuers. An applet like this can add a deeper policy layer for institutions that need hardware-rooted transaction control.

02Corporate Treasury Wallets

Companies often need to move tokens between treasury accounts, exchanges, liquidity providers, payroll wallets, or operating wallets. But they may not want every authorized operator to sign arbitrary transactions.

A card with on-card EVM filtering can enforce: only approved ERC-20 contracts, only transfer/approve/transferFrom, only up to a configured amount, and only no-ETH-value contract calls.

That makes it suitable for treasury cards issued to finance teams, trading desks, or regional operators. The key remains on-card, but the transaction policy also moves on-card.

03DeFi Approval Risk Reduction

Unlimited ERC-20 approvals are a recurring source of wallet risk. A malicious or compromised frontend may encourage users to approve excessive token allowances. Unlimited approvals are common in the Ethereum ecosystem, and every one of them is a standing permission that a compromised contract or frontend can drain.

This applet can enforce maximum approval amounts directly in the secure element. Even if the host tries to send a larger approve() transaction, the card rejects it.

Particularly relevant for: approve(), Permit2-style approvals, DEX order signing, vault interactions, and liquidity management.

04White-Label Hardware Wallets for Fintechs and Banks

A bank, neobank, or fintech may want a hardware wallet card that supports self-custodial signing but does not allow arbitrary Web3 activity.

A custom version of this applet could allow the institution to define a controlled contract universe: approved stablecoin contracts, payment routers, DEX aggregators, recovery or policy contracts, and governance contracts.

This enables a middle ground between a fully open hardware wallet and a closed custodial system. The user holds the key, but the card enforces a product-specific policy.

05Kiosk, POS, and Embedded Signing Devices

Some systems need to sign predictable blockchain transactions from a constrained or semi-unattended environment — merchant stablecoin payment terminals, Web3 ticketing kiosks, machine-to-machine token payments, industrial IoT settlement devices, and closed-loop token payment systems.

In these cases, the host device should not be trusted with arbitrary signing power. The card can act as a secure signing module that only authorizes a limited class of token transactions. The 48 ms ECDSA/secp256k1 signing time makes the EVM path practical for interactive payment and terminal-style workflows.

06DAO and Governance Signing

EIP-712 support makes the applet relevant for structured governance messages. A DAO, protocol foundation, or enterprise governance system could configure the card to sign typed data only for approved governance contracts.

The same whitelist that protects EIP-1559 contract calls also protects EIP-712 verifying contracts, helping separate legitimate voting or delegation flows from unrelated typed-data phishing attempts.

07Dual-Curve Secure Element Projects

The inclusion of Ed25519 sign and verify makes the applet more than an EVM-only project. Many modern blockchain, identity, and authentication systems rely on Ed25519. By implementing Ed25519 and secp256k1 on the same card, the project shows how one secure element can support multiple cryptographic ecosystems.

That matters for: multi-chain hardware wallets, identity credentials, device authentication, protocol bridges, custom blockchain networks, and hybrid EVM / non-EVM products.

APDU Command Structure

All commands use:

CLA = 0x80

The applet exposes four main command groups.

Key Management

GET_VERSION       INS 0x10 P1 0x00
GET_CAPS          INS 0x10 P1 0xE0
GET_PUBLIC_KEY    INS 0x12 P1 0x00
GET_ADDRESS       INS 0x12 P1 0x01
KEY_GEN           INS 0x14 P1 0x00  [SCP03]
SET_SK            INS 0x52 P1 0x00  [SCP03]
WIPE_KEY          INS 0x54 P1 0x00  [SCP03]

KEY_GEN generates the private key on-card and returns the public key. SET_SK allows secure private key injection through SCP03. WIPE_KEY zeros all key material and clears the whitelist.

Whitelist Management

SET_WHITELIST_ENTRY   INS 0x32 P1 0x00-0x04  [SCP03]
UPDATE_ENTRY_LIMIT    INS 0x32 P1 0x10       [SCP03]
GET_WHITELIST_ENTRY   INS 0x34 P1 0x00-0x04
GET_ALL_WHITELIST     INS 0x34 P1 0xFF
REMOVE_ENTRY          INS 0x36 P1 0x00-0x04  [SCP03]
CLEAR_ALL_WHITELIST   INS 0x36 P1 0xFF       [SCP03]

Whitelist entries are compact, fixed-size records:

flags(1) + address(20) + max_amount(12)

Transaction Signing

SIGN_TX_INIT      INS 0x42
SIGN_TX_UPDATE    INS 0x44
SIGN_TX_FINAL     INS 0x46
SIGN_TYPED_DATA   INS 0x48

The host streams the EIP-1559 transaction to the card in chunks. The card validates policy and returns the signature only if the transaction passes all checks.

Development Diagnostics

The diagnostic command set includes test commands for Keccak-256, secp256k1 public key derivation, deterministic test signatures, SHA-512, Ed25519 public key derivation, Ed25519 signing, and Ed25519 sign-and-verify.

These commands are development-oriented and should not be exposed in a production personalization profile unless explicitly required for manufacturing or certification testing.

Performance Results

The project achieved the following measured performance.

~48 msEIP-1559 signing
(ECDSA / secp256k1)
16 msPublic key derivation
(secp256k1)
1561 msEd25519 signing
(software arithmetic)
4063 msEd25519 verification
(software arithmetic)

ECDSA / secp256k1 for EVM

OperationTime
EIP-1559 signing~48 ms
Public key derivation16 ms

This is fast enough for real-world card-based transaction approval.

Ed25519

OperationTime
Public key derivation766 ms
Signing1561 ms
Verification4063 ms

Ed25519 is heavier in this implementation because field arithmetic over p = 2^255 − 19 is performed in software. The applet uses schoolbook multiplication with sparse prime reduction, with FAME3 used only for scalar reduction modulo ℓ.

Cryptographic Optimizations

The project includes several important optimizations.

For secp256k1, the applet uses the GLV endomorphism to decompose a 256-bit scalar into two 128-bit half-scalars, combined with Shamir's trick for simultaneous scalar multiplication. It also uses software field multiplication optimized for the secp256k1 sparse prime:

2^256 = 2^32 + 977

For Ed25519, the applet includes:

  • comb d=6 fixed-base scalar multiplication
  • wNAF w=4 variable-base scalar multiplication
  • dedicated squaring
  • normalized Niels point representation

For nonce generation, the project uses an AES-128 based CSPRNG seeded from the P71 hardware TRNG. Each signature derives:

k = keccak256(seed || digest)

The seed is updated after every use, giving forward secrecy while keeping the per-signature overhead negligible.

Raw Keccak-256, Not SHA3-256

Ethereum uses the raw Keccak variant, not the finalized NIST SHA3-256 padding variant.

Implementation Note

This applet uses raw Ethereum-style Keccak-256 with padding byte 0x01, not SHA3-256 with padding byte 0x06. That distinction is critical for Ethereum transaction compatibility.

The Keccak specification documents the sponge construction and the domain separation suffixes used by standardized SHA-3 instances.

Memory Footprint on JCOP P71

Measured on 2026-03-23 via run_jcop_info.py, the clean card baseline was:

PHEAP = 344,204
COR   = 4,625
COD   = 4,624

After installation:

AID:       A000000647200201
State:     SELECTABLE
CAP size:  33,161 bytes

Resource usage:

PHEAP used: 34,176 bytes
COR used:    4,608 bytes
COD used:    4,608 bytes

This makes it one of the largest PHEAP-consuming custom applet projects, which is expected given the combination of secp256k1, Keccak-256, RLP parsing, policy enforcement, and Ed25519.

Constant-Time Signing and Side-Channel Hardening

The signing path was designed to avoid secret-dependent timing behavior.

The implementation includes:

  • fixed-iteration Fermat inversion for Z^-1 mod p
  • blinded GCD for k^-1 mod n
  • arithmetic-mask conditional moves
  • constant-time low-S normalization
  • constant-time GLV negation handling
  • point-at-infinity protection
  • volatile barriers against compiler branch reconstruction

This matters because secure elements are often used in hostile physical environments. A card may be probed, timed, powered repeatedly, or attacked through malformed transaction requests. A production-grade signing applet must therefore consider not only mathematical correctness, but also implementation-level leakage.

Build and Deployment Flow

The project build flow is split into three stages:

# 1. Native C library
.\build_native.ps1

# 2. Java applet
.\build_java.ps1

# 3. Merge CAP + native
.\merge_cap.ps1

Deployment is performed through JCShell:

scripts/loadAndInstall.jcsh

After testing, cleanup is mandatory:

scripts/deleteApplet.jcsh

This reflects a hybrid JavaCard and SecureBox development model: JavaCard manages the applet lifecycle and APDU interface, while SecureBox native C handles cryptographic primitives and performance-critical operations.

What This Project Demonstrates

This project shows that a secure element can enforce application-level blockchain policy, not merely store keys.

For EVM applications, that is a major architectural difference. The card can verify the transaction class, contract destination, calldata type, amount, access list, and typed-data verifying contract before producing a signature.

For fintechs, banks, enterprise treasury teams, and blockchain infrastructure providers, this creates a path toward hardware-rooted transaction governance. It is the on-card counterpart of the server-side policy checks that the Card Wallet as a Service platform already enforces for issuers.

The key question becomes less: "Can the card sign?" — and more: "Can the card know enough to refuse unsafe signatures?"

With this EVM Transaction Filtering Applet, the answer is yes.

Conclusion

JavaCard custom development is no longer limited to conventional PKI, payment, or identity applets. With NXP JCOP 4.5 P71 and SecureBox native C, it is possible to build advanced blockchain-specific secure element applications that combine private key protection, custom cryptography, transaction parsing, and policy enforcement.

The EVM Transaction Filtering Applet is a strong example of that direction.

It supports EIP-1559 transaction signing, raw Keccak-256, secp256k1, ERC-20 contract whitelisting, per-entry amount limits, EIP-712 typed data signing, and Ed25519 dual-curve capability on the same card.

For use cases where signing must be both self-custodial and policy-controlled, this kind of applet can provide the missing layer: transaction intelligence inside the secure element itself.

To learn more about Cryptnox's custom JavaCard and secure element development services, get in touch with our team.

Need a custom applet on a secure element?

Cryptnox designs, builds and deploys JavaCard and native secure-element applets for wallets, payments, identity and access control.

Frequently Asked Questions

What is on-card EVM transaction filtering?

It means the secure element receives the full transaction, not just a 32-byte digest, and checks it against a policy before signing. In this applet the card RLP-parses an EIP-1559 transaction, verifies the destination contract, the ERC-20 function selector, the amount and the access list, computes the Keccak-256 hash itself and only then produces the secp256k1 signature.

Which Ethereum transaction types does the applet sign?

EIP-1559 type 0x02 transactions only, plus EIP-712 typed structured data. Native ETH transfers are rejected by design: the value field must be zero, the calldata must be non-empty and the destination must be a whitelisted contract.

What happens if no whitelist is configured?

The card refuses to sign. The applet is fail-closed: it must be explicitly configured with at least one whitelist entry (a 20-byte contract address, a uint96 amount limit and policy flags) before it will authorize any contract call.

Does the whitelist also apply to EIP-712 signatures?

Yes. For typed data the host sends the verifying contract address, the domain separator and the struct hash; the card checks the verifying contract against the same whitelist used for transactions, so no second policy store is needed.

How fast is on-card signing on the JCOP 4.5 P71?

Measured on the target card, an EIP-1559 signature with ECDSA/secp256k1 takes about 48 ms and secp256k1 public key derivation 16 ms. Ed25519 is slower (766 ms key derivation, 1,561 ms signing, 4,063 ms verification) because its field arithmetic runs in software.

Why does the applet use raw Keccak-256 rather than SHA3-256?

Ethereum hashes with the original Keccak padding byte 0x01, while the NIST SHA3-256 standard uses 0x06. Using SHA3-256 would produce a different digest and an invalid Ethereum signature, so the applet implements the raw Keccak variant.

Can Cryptnox build a similar applet for my product?

Yes. This project is one example of Cryptnox's custom smart card development work: JavaCard applets, SecureBox native C, blockchain-specific cryptography, APDU interface design and full-stack integration, delivered on NXP JCOP P71 secure elements. Use the contact page to describe your use case.

Policy inside the secure element, not around it

The question this project answers is not "can the card sign?" but "can the card know enough to refuse an unsafe signature?". With RLP parsing, contract whitelisting, amount limits, EIP-712 support and constant-time secp256k1 on a JCOP 4.5 P71, the answer is yes, at interactive speed.

Start a secure element project

Tell us the chain, the policy and the form factor. We will scope the applet, the host integration and the personalization flow.

Sources

Performance and memory figures are measurements taken on the project's target card (NXP JCOP 4.5 P71) in March 2026; results on other cards, firmware or applet configurations will differ. Certification statements refer to the NXP secure element platform, not to the applet or the finished card. Cryptnox cards are made in Switzerland.