Files
aliro-project/applet/src/main/java/com/dangerousthings/aliro/CryptoSingletons.java
michael 96816bc232 feat(applet): StepUpApplet INS_EXCHANGE decrypt-and-discard with 9000 ack
X-CUBE-ALIRO firmware sends a Reader Status sub-event via EXCHANGE
(CLA=0x80, INS=0xC9) after the step-up AID SELECT, per Aliro §8.3.3.5
Table 8-14. The payload is encrypted with StepUpSKReader using AES-256-GCM
with IV = 0x0000000000000000 || stepup_reader_counter (4B BE) per §8.3.1.8.

For Milestone 1 we decrypt-and-discard: tag verification proves we have
matching session keys, then we return SW=9000 with empty payload. M2
will add a proper encrypted Reader Status response sub-event.

Extends CryptoSingletons to hold the shared AliroGcm instance too --
opt 1 sibling of the AliroCrypto sharing from M1A.2. Adds AliroGcm.decrypt
since the prior pipeline only encrypted (AUTH1 response path).

Counter starts at 1 per session-bound init (spec §8.4.3 -> mdoc [6]
§9.1.1.5), incremented after each successful decrypt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 10:45:22 -07:00

46 lines
1.8 KiB
Java

package com.dangerousthings.aliro;
/**
* Lazy holders for crypto objects shared between {@link AliroApplet} and
* {@link StepUpApplet}. Both applets live in the same package and only one
* is selected at a time, so the shared instances are safe — Java Card runs
* one applet at a time and the JCRE serializes APDU dispatch.
*
* <p>Java Card forbids {@code new} in {@code <clinit>}, so the getter lazy-
* creates on first call. Subsequent calls return the cached instance. The
* combined transient footprint of one shared {@link AliroCrypto} is roughly
* 352 B (kdfWorkbuf 64 + hkdfPrevT 32 + expandScratch 256); without this
* sharing each applet would allocate its own.
*/
final class CryptoSingletons {
private static AliroCrypto aliroCrypto;
private static AliroGcm aliroGcm;
private CryptoSingletons() { }
/** Returns the process-wide {@link AliroCrypto} instance. Allocates on
* first call; cheap thereafter. */
static AliroCrypto getAliroCrypto() {
if (aliroCrypto == null) {
aliroCrypto = new AliroCrypto();
}
return aliroCrypto;
}
/** Returns the process-wide {@link AliroGcm} instance. Same Java-Card
* {@code <clinit>}-cannot-{@code new} rationale as {@link #getAliroCrypto()}:
* lazy-allocate on first call so both {@link AliroApplet} and
* {@link StepUpApplet} share one userland-GCM machine. Each shared
* instance saves ~370 B of transient/EEPROM footprint that a per-applet
* duplicate would otherwise pay. The two applets are never selected
* simultaneously and the JCRE serializes APDU dispatch, so the shared
* scratch and {@code AESKey} slot don't race. */
static AliroGcm getAliroGcm() {
if (aliroGcm == null) {
aliroGcm = new AliroGcm();
}
return aliroGcm;
}
}