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.
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 || sWhere:
- 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:
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:
tomust match one configured 20-byte contract addressvaluemust be 0datamust be non-emptyaccessListmust 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 68If 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:
domainSeparatorstructHash
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 = 0x80The 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 0x48The 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.
(ECDSA / secp256k1)
(secp256k1)
(software arithmetic)
(software arithmetic)
ECDSA / secp256k1 for EVM
| Operation | Time |
|---|---|
| EIP-1559 signing | ~48 ms |
| Public key derivation | 16 ms |
This is fast enough for real-world card-based transaction approval.
Ed25519
| Operation | Time |
|---|---|
| Public key derivation | 766 ms |
| Signing | 1561 ms |
| Verification | 4063 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 + 977For 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.
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,624After installation:
AID: A000000647200201
State: SELECTABLE
CAP size: 33,161 bytesResource usage:
PHEAP used: 34,176 bytes
COR used: 4,608 bytes
COD used: 4,608 bytesThis 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.ps1Deployment is performed through JCShell:
scripts/loadAndInstall.jcshAfter testing, cleanup is mandatory:
scripts/deleteApplet.jcshThis 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.